Go指南 - 笔记
标签(空格分隔): Go
一、基础
1.包
每个Go程序都是由包构成的。
程序从main包开始运行。
包名与导入路径的最后一个元素一致
2.导入
分组导入:使用圆括号组合导入。推荐分组导入。
3.导出名
在Go中,如果一个名字以大写字母开头,那么它就是已导出的。
只能导入已导出的名字;任何未导出的名字在该包外均无法访问。
4.函数
函数可以没有参数或接受多个参数。
类型在变量名之后。
当连续两个或多个函数的已命名形参类型相同时,除最后一个外,其他的都可省略。
func add (x, y int) int {
return x + y
}
函数可以返回多值:
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
}
Go的返回值可被命名,它们会被视作定义在函数顶部的变量。
没有参数的return
语句返回已命名的返回值。
仅用在短函数中,否则影响代码可读性。
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
5.变量
var
语句用于声明一个变量列表,类型在表达式后面。
多个连续相同的变量也可以按照函数参数一样的方式只在最后一个指定类型。
变量声明可以包含初始值,每个变量对应一个。如果初始值已存在,则可以省略类型。
var i, j int = 1, 2
只能在函数中使用:=
在类型明确的地方代替var
声明。
6.基本类型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8
rune // int32
float32 float64
complex64 complex128
int
,uint
,uintptr
在32位系统上通常为32位宽,64位系统上则为64位宽。
没有明确初始值的变量声明会被赋予它们的零值:
- 数值类型为
0
- 布尔类型为
false
- 字符串为
""
(空字符串)
表达式T(v)
将值v
转换为类型T
。
i := 3
f := float64(i)
7.常量
使用const
关键字。不能使用:=
语法。
二、流程控制语句
1.for
Go只有一种循环结构:
for
循环。
基本的for
循环由三部分组成,它们用分号隔开:
初始化语句:在每一次迭代前执行;通常为短变量声明,该变量仅在for
语句作用域中可见
条件表达式:在每次迭代前求值;一旦表达式的布尔值为false
,迭代就会终止
后置语句:在每次迭代的结尾执行
for i := 0; i < 10; i++ { }
// 初始化语句和后置语句是可选的
// 相当于while循环
j := 1
for j < 10 { }
// 无限循环
for { }
Go的for语句后面的三个构成部分外没有小括号,大括号
{}
则是必须的。
2.if
Go的
if
语句与for
循环类似,表达式外无需小括号,而大括号{}
是必须的。
if
语句可以在条件表达式之前执行一个简单的语句,该语句声明变量的作用域仅在if
之内(包括它的else块)。
if y := x * x; y > 100 { }
3.switch
Go会自动终止case
分支,想要不终止需要fallthrough
语句结束。
case
可以是表达式。
switch
的case
语句从上到下顺次执行,直到匹配成功时停止。
switch
的条件可以为true
,或者说没有条件;此时相当于if else
语句。
4.defer
defer
语句会将函数推迟到外层函数返回之后执行。
推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。
defer fmt.Println("world")
fmt.Println("hello")
推迟的函数调用会被压入一个栈中,当外层函数返回时,被推迟的函数会按照先进后出的顺序调用。
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
// 结果: 9 8 7 6 5 4 3 2 1 0
三、struct、slice和映射等
1.指针
Go拥有指针。指针保存了值的内存地址。
Go没有指针运算。
类型*T
是指向T
类型值的指针。其零值为nil
。
var p *int
fmt.Println(p)
// <nil>
&
操作符会生成一个指向其操作数的指针。
i := 1
p = &i
fmt.Println(p)
// 0x416038
*
操作符表示指针指向的底层值。这就是通常所说的“间接引用”或“重定向”。
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
2.结构体(struct)
一个结构体(struct)就是一组字段(field)。
结构体字段用点号来访问。
type Vertex struct {
X int
Y int
}
func main() {
fmt.Printin(Vertex{1, 2}) // {1 2}
v.X = 4
fmt.Println(v.X) // 4
}
结构体字段可以通过结构体指针来访问。
允许隐式间接引用(不写*
号):
v := Vertex{1, 2}
p := &v
fmt.Println(p.X) // 1
3.数组
类型[n]T
表示拥有n个T类型的值的数组。
切片为数组元素提供动态大小的、灵活的视角。切片比数组更常用。
类型[]T
表示一个元素类型为T
的切片。
切片通过两个下标来界定,即一个上界和一个下界,二者以冒号分隔:
arr[low: high]
它会选择一个半开区间,包括第一个元素,排除最后一个元素。
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
fmt.Println(s)
// [3 5 7]
切片就像数组的引用
切片并不存储任何数据,它只是描述了底层数组中的一段。
更改切片的元素会修改其底层数组中对应的元素。
与它共享底层数组的切片都会观测到这些修改。
切片文法类似于没有长度的数组文法。
[]bool {true, false, true}
上面的代码会创建一个数组,然后构建了这个引用这个数组的一个切片。
下面是一个结构体切片::
s := []struct {
i int
b string
} {
{1, "a"},
{2, "b"},
{3, "c"},
{4, "d"},
{5, "e"},
}
fmt.Println(s)
// [{1 a} {2 b} {3 c} {4 d} {5 e}]
在进行切片时,可以利用它的默认行为来忽略上下界。
切片下界默认值为0,上界则是该切片的长度。
对于数组:
var a [10]int
// 以下切片是等价的
a[0:10]
a[:10]
a[0:]
a[:]
切片拥有长度和容量。
切片的长度就是它所包含的元素的个数。
切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
切片的零值是ni