数组长度固定、切片动态长度
1| // 假设T为任意一个类型,Tkey为一个支持比较的类型。
2|
3| *T // 一个指针类型
4| [5]T // 一个元素类型为T、元素个数为5的数组类型
5| []T // 一个元素类型为T的切片类型
6| map[Tkey]T // 一个键值类型为Tkey、元素类型为T的映射类型
7|
8| // 一个结构体类型
9| struct {
10| name string
11| age int
12| }
13|
14| // 一个函数类型
15| func(int) (bool, string)
16|
17| // 一个接口类型
18| interface {
19| Method0(string) int
20| Method1() (int, bool)
21| }
22|
23| // 几个通道类型
24| chan T
25| chan<- T
26| <-chan T
在Go中,一个无名指针类型的字面形式为*T,其中T为一个任意类型。类型T称为 指针类型*T的基类型(base type)。 如果一个指针类型的基类型为T,则我们可 以称此指针类型为一个T指针类型。
1| *int // 一个基类型为int的无名指针类型。
2| **int // 一个多级无名指针类型,它的基类型为*int。
3|
4| type Ptr *int // Ptr是一个具名指针类型,它的基类型为int。
5| type PP *Ptr // PP是一个具名多级指针类型,它的基类型为Ptr。
指针类型的零值的字面量用预声明的nil来表示。nil指针(常称为空指针)中不存储任何地址。
如果一个指针类型的基类型为T,则此指针类型的值只能存储类型为T的值的地址。
可以用前置取地址操作符&来获取一个可寻址值的地址。 对于一 个类型为T的可寻址的值t,我们可以用&t来取得它的地址。&t的类型为 *T
//有关解引用 值传递
1| package main
2|
3| import "fmt"
4|
5| func main() {
6| p0 := new(int) // p0指向一个int类型的零值
7| fmt.Println(p0) // (打印出一个十六进制形式的地址)
8| fmt.Println(*p0) // 0
9|
10| x := *p0 // x是p0所引用的值的一个复制。
11| p1, p2 := &x, &x // p1和p2中都存储着x的地址。
12| // x、*p1和*p2表示着同一个int值。
13| fmt.Println(p1 == p2) // true
14| fmt.Println(p0 == p1) // false
15| p3 := &*p0 // <=> p3 := &(*p0)
16| // <=> p3 := p0
17| // p3和p0中存储的地址是一样的。
18| fmt.Println(p0 == p3) // true
19| *p0, *p1 = 123, 789
20| fmt.Println(*p2, x, *p3) // 789 789 123
21|
22| fmt.Printf("%T, %T \n", *p0, x) // int, int
23| fmt.Printf("%T, %T \n", p0, p1) // *int, *int
24| }
看懂就行啦 其实就是创建修改了指针副本,但指针副本也是指向原指针的地址值,对副本的修改也会对原指针的地址值生效
1| package main
2|
3| import "fmt"
4|
5| func double(x *int) {
6| *x += *x
7| x = nil // 此行仅为讲解目的
8| }
9|
第15章:指针
123
10| func main() {
11| var a = 3
12| double(&a)
13| fmt.Println(a) // 6
14| p := &a
15| double(p)
16| fmt.Println(a, p == nil) // 12 false
17| }
Go支持垃圾回收,所以一个函数返回其内声明的局部变量的地址是 绝对安全的。比如:
1| func newInt() *int {
2| a := 3
3| return &a
4| }
Go指针值是支持(使用比较运算符==和!=)比较的。 但是,两个指针只有在下列任一条件被满足的时候才可以比较:
一个结构体类型的尺寸为它的所有字段的(类型)尺寸之和加上一些填充字节的数目。编译器会在一个结构体值的两个相邻字段之间填充一些字节来保证一些字段的地址总是某个整数的倍数。
结构体值的赋值
1| func f() {
2| book1 := Book{pages: 300}
3| book2 := Book{"Go语言101", "老貘", 256}
4|
5| book2 = book1
6| // 上面这行和下面这三行是等价的。
7| book2.title = book1.title
8| book2.author = book1.author
9| book2.pages = book1.pages
10| }
如果一个结构体值是可寻址的,那它的字段也是可寻址的;反之,不可寻址的结构体值的字段也是不可寻址的。不可寻址的字段的值是不可更改的。所有组合的字面量都是不可寻址的。
可以比,但没必要
两个类型分别为S1和S2的结构体值只有在S1和S2的底层类型相同(忽略掉字段标 签)的情况下才能相互转换为对方的类型。 特别地,如果S1和S2的底层类型相同 (要考虑字段标签)并且只要它们其中有一个为无名类型,则此转换可以是隐式 的。
“引用”这个术语在Go社区中使用得有些混乱。很多Go程序员在Go编程中可能由此 产生了一些困惑。 一些文档或者网络文章,包括一些官方文档 (https://golang.google.cn/doc/faq#references),把“引用”(reference) 看作是“值”(value)的一个对立面。 《Go语言101》强烈不推荐这种定义。在这 一点上,本人不想争论什么。这里仅仅列出一些肯定错误地使用了“引用”这个术 语的例子:
我并不是想说引用类型这个术语在Go中是完全没有价值的, 我只是想表达这个术语 是完全没有必要的,并且它常常在Go的使用中导致一些困惑。我推荐使用指针持有者类型来代替这个术语。 另外,我个人的观点是最好将引用这个词限定到只表示值 之间的关系,把它当作一个动词或者名词来使用,永远不要把它当作一个形容词来 使用。 这样将在使用Go的过程中避免很多困惑。