Go 指南
更多类型
Go 拥有指针。指针保存了值的内存地址。
类型 *T
是指向 T
类型值的指针。其零值为 nil
。
&
操作符会生成一个指向其操作数的指针。
*
操作符表示指针指向的底层值。
1 2
| fmt.Println(*p) *p = 21
|
这也就是通常所说的“间接引用”或“重定向”。
与 C 不同,Go 没有指针运算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import "fmt"
func main() { i, j := 42, 2701
p := &i fmt.Println(*p) *p = 21 fmt.Println(i)
p = &j *p = *p / 37 fmt.Println(j) }
|
一个结构体(struct
)就是一组字段(field
)。
1 2 3 4 5 6 7 8 9 10 11 12
| package main
import "fmt"
type Vertex struct { X int Y string }
func main() { fmt.Println(Vertex{111, "asd"}) }
|
结构体字段使用点号来访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package main
import "fmt"
type Vertex struct { X int Y int }
func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) }
|
结构体字段可以通过结构体指针来访问。
如果我们有一个指向结构体的指针 p
,那么可以通过 (*p).X
来访问其字段 X
。不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X
就可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package main
import "fmt"
type Vertex struct { X int Y int }
func main() { v := Vertex{1, 2} p := &v p.X = 1e9 fmt.Println(v) }
|
结构体文法通过直接列出字段的值来新分配一个结构体。
使用 Name:
语法可以仅列出部分字段。(字段名的顺序无关。)
特殊的前缀 &
返回一个指向结构体的指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main
import "fmt"
type Vertex struct { X, Y int }
var ( v1 = Vertex{1, 2} v2 = Vertex{X: 1} v3 = &Vertex{} p = &Vertex{1, 2} )
func main() { fmt.Println(v1, p, v2, v3) }
|
类型 [n]T
表示拥有 n
个 T
类型的值的数组。
表达式
会将变量 a
声明为拥有 10 个整数的数组。
数组的长度是其类型的一部分,因此数组不能改变大小。这看起来是个限制,不过没关系,Go 提供了更加便利的方式来使用数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package main
import "fmt"
func main() { var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a)
primes := [6]int{2, 3, 5, 7, 11, 13} fmt.Println(primes) }
|
每个数组的大小都是固定的。而切片则为数组元素提供动态大小的、灵活的视角。在实践中,切片比数组更常用。
类型 []T
表示一个元素类型为 T
的切片。
切片通过两个下标来界定,即一个上界和一个下界,二者以冒号分隔:a[low : high]
它会选择一个半开区间,包括第一个元素,但排除最后一个元素。
以下表达式创建了一个切片,它包含 a
中下标从 1 到 3 的元素:a[1:4]
1 2 3 4 5 6 7 8 9 10
| package main
import "fmt"
func main() { primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4] fmt.Println(s) }
|
切片并不存储任何数据,它只是描述了底层数组中的一段。
更改切片的元素会修改其底层数组中对应的元素。
与它共享底层数组的切片都会观测到这些修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package main
import "fmt"
func main() { names := [4]string{ "John", "Paul", "George", "Ringo", } fmt.Println(names)
a := names[0:2] b := names[1:3] fmt.Println(a, b)
b[0] = "XXX" fmt.Println(a, b) fmt.Println(names) }
|
切片文法类似于没有长度的数组文法。
这是一个数组文法:
1
| [3]bool{true, true, false}
|
下面这样则会创建一个和上面相同的数组,然后构建一个引用了它的切片:
1
| []bool{true, true, false}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package main
import "fmt"
func main() { q := []int{2, 3, 5, 7, 11, 13} fmt.Println(q)
r := []bool{true, false, true, true, false, true} fmt.Println(r)
s := []struct { i int b bool }{ {2, true}, {3, false}, {5, true}, {7, true}, {11, false}, {13, true}, } fmt.Println(s) }
|
在进行切片时,你可以利用它的默认行为来忽略上下界。
切片下界的默认值为 0
,上界则是该切片的长度。
对于数组
来说,以下切片是等价的:
1 2 3 4
| a[0:10] a[:10] a[0:] a[:]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main
import "fmt"
func main() { s := []int{2, 3, 5, 7, 11, 13}
s = s[1:4] fmt.Println(s)
s = s[:2] fmt.Println(s)
s = s[1:] fmt.Println(s) }
|
切片的零值是 nil
。
nil 切片的长度和容量为 0 且没有底层数组。
1 2 3 4 5 6 7 8 9 10 11
| package main
import "fmt"
func main() { var s []int fmt.Println(s, len(s), cap(s)) if s == nil { fmt.Println("nil!") } }
|
切片可以用内建函数 make
来创建,这也是你创建动态数组的方式。
make
函数会分配一个元素为零值的数组并返回一个引用了它的切片:
要指定它的容量,需向 make
传入第三个参数:
1 2 3 4
| b := make([]int, 0, 5)
b = b[:cap(b)] b = b[1:]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import "fmt"
func main() { a := make([]int, 5) printSlice("a", a)
b := make([]int, 0, 5) printSlice("b", b)
c := b[:2] printSlice("c", c)
d := c[2:5] printSlice("d", d) }
func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) }
|