指针
Go 拥有指针。指针保存了值的内存地址。
类型 *T 是指向 T 类型值的指针。其零值为 nil。
var p *int
& 操作符会生成一个指向其操作数的指针。
i := 42
p = &i
- 操作符表示指针指向的底层值。
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21 // 通过指针 p 设置 i
这也就是通常所说的“间接引用”或“重定向”。
与 C 不同,Go 没有指针运算。
结构体
- 结构体声明
type Vertex struct {
X int
Y int
}
- 结构体初始化
v := Vertex{1, 2}
- 结构体数据引用(结构体字段使用点号来访问)
v.x
- 结构体指针
- 如果我们有一个指向结构体的指针 p,那么可以通过 (*p).X 来访问其字段 X。不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以
p := &v
p.x
- 结构体文法通过直接列出字段的值来新分配一个结构体
var (
v1 = Vertex{1, 2} // 创建一个 Vertex 类型的结构体
v2 = Vertex{X: 1} // Y:0 被隐式地赋予
v3 = Vertex{} // X:0 Y:0
p = &Vertex{1, 2} // 创建一个 *Vertex 类型的结构体(指针)
)
数组
- 声明
//类型 [n]T 表示拥有 n 个 T 类型的值的数组
var a [10]int //数组a表示长度为10,内容为int类型,注意数组长度不可变
- 赋值
//先声明,后赋值
var a [2] int
a[0] = 1
a[1] = 2
// 直接声明赋值
b := [2]int{1, 2}
- 切片
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
// 更改切片的元素会修改其底层数组中对应的元素。
- 切片文法
r := []int{1, 2}
- 切片长度获取
len(r)
- 切片容量获取
cap(r)
- 切片的零值是
nil
- 用
make
来创建切片
a := make([]int, 5) // len(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
- 切片可包含任何类型,甚至包括其它的切片。
- 向切片追加元素(
append
)
a := make([]int, 5)
a = append(a, 0) // 添加0进入切片
a = append(a, 0, 1, 2, 3) // 添加0,1,2,3进入切片
- range
for 循环的 range 形式可遍历切片或映射。
当使用 for 循环遍历切片时,每次迭代都会返回两个值。
第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。
映射
- 映射的零值为
nil
。nil
映射既没有键,也不能添加键 - 映射的文法
type Example struct {
name string
}
var m map[string]Example
m = make(map[string]Example) //make 函数会返回给定类型的映射,并将其初始化备用
m["0"] = Example{
"jack", // `,`必不可少
}
fmt.Println(m) // map[0:{jack}]
- 修改映射
在映射 m 中插入或修改元素:
m[key] = elem
获取元素:
elem = m[key]
删除元素:
delete(m, key)
通过双赋值检测某个键是否存在:
elem, ok = m[key]
若 key 在 m 中,ok 为 true ;否则,ok 为 false。
若 key 不在映射中,那么 elem 是该映射元素类型的零值。
同样的,当从映射中读取某个不存在的键时,结果是映射的元素类型的零值。
注 :若 elem 或 ok 还未声明,你可以使用短变量声明:
elem, ok := m[key]
- 函数也是值。它们可以像其它值一样传递
var show = func (name string) {
fmt.Println(name)
}
var container (fn func(name string)) {
fn("container")
}
container(show)
- 闭包
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
这个例子可以得知,sum会被后面的闭包沿用,并不是每次调用pos重新赋值为0