1. Go语言的出现
在具体学习go语言的基础语法之前,我们来了解一下go语言出现的时机及其特点。
Go语言最初由Google公司的Robert Griesemer、Ken Thompson和Rob Pike三个大牛于2007年开始设计发明,他们最终的目标是设计一种适应网络和多核时代的C语言。所以Go语言很多时候被描述为“类C语言”,或者是“21世纪的C语言”,当然从各种角度看,Go语言确实是从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等诸多编程思想。但是Go语言更是对C语言最彻底的一次扬弃,它舍弃了C语言中灵活但是危险的指针运算,还重新设计了C语言中部分不太合理运算符的优先级,并在很多细微的地方都做了必要的打磨和改变。
2. Go语言组成
go语言的基本组成有:
- 包声明,编写源文件时,必须在非注释的第一行指明这个文件属于哪个包,如
package main。 - 引入包,其实就是告诉Go 编译器这个程序需要使用的包,如
import "fmt"其实就是引入了fmt包。 - 函数,和c语言相同,即是一个可以实现某一个功能的函数体,每一个可执行程序中必须拥有一个main函数。
- 变量,Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
- 语句/表达式,在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。
- 注释,和c语言中的注释方式相同,可以在任何地方使用以 // 开头的单行注释。以 /* 开头,并以 */ 结尾来进行多行注释,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
3. Go语言重要特性
3.0 slice的引入
简单地说,切片就是一种简化版的动态数组。因为动态数组的长度不固定,切片的长度自然也就不能是类型的组成部分了。数组虽然有适用它们的地方,但是数组的类型和操作都不够灵活,而切片则使用得相当广泛。
切片高效操作的要点是要降低内存分配的次数,尽量保证append操作(在后续的插入和删除操作中都涉及到这个函数)不会超出cap的容量,降低触发内存分配的次数和每次分配内存大小。
3.1 slice的定义
我们先看看切片的结构定义,reflect.SliceHeader:
type SliceHeader struct {
Data uintptr // 指向底层的的数组指针
Len int // 切片长度
Cap int // 切片最大长度
} 和数组一样,内置的len函数返回切片中有效元素的长度,内置的cap函数返回切片容量大小,容量必须大于或等于切片的长度。
切片可以和nil进行比较,只有当切片底层数据指针为空时切片本身为nil,这时候切片的长度和容量信息将是无效的。如果有切片的底层数据指针为空,但是长度和容量不为0的情况,那么说明切片本身已经被损坏了
只要是切片的底层数据指针、长度和容量没有发生变化的话,对切片的遍历、元素的读取和修改都和数组是一样的。在对切片本身赋值或参数传递时,和数组指针的操作方式类似,只是复制切片头信息(reflect.SliceHeader),并不会复制底层的数据。对于类型,和数组的最大不同是,切片的类型和长度信息无关,只要是相同类型元素构成的切片均对应相同的切片类型。
当我们想定义声明一个切片时可以如下:
在对切片本身赋值或参数传递时,和数组指针的操作方式类似,只是复制切片头信息·(reflect.SliceHeader),并不会复制底层的数据。对于类型,和数组的最大不同是,切片的类型和长度信息无关,只要是相同类型元素构成的切片均对应相同的切片类型。
4.函数
为完成某一功能的程序指令(语句)的集合,称为函数。
在Go语言中,函数是第一类对象,我们可以将函数保持到变量中。函数主要有具名和匿名之分,包级函数一般都是具名函数,具名函数是匿名函数的一种特例,当匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心。
具名函数
func Add(a, b int) int {
return a+b
}
匿名函数
var Add = func(a, b int) int {
return a+b
}
闭包函数:返回为函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
一级对象:支持闭包的多数语言都将函数作为第一级对象,就是说函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回。
包:go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。
5. 实践
此案例是一个猜谜游戏
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
// fmt.Println("The secret number is ", secretNumber)
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
for {
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
continue
}
input = strings.Trim(input, "\r\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
continue
}
fmt.Println("You guess is", guess)
if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
break
}
}
}