1.2.2 指针
Go语言是支持指针的,这一点和C/C++很像。假设有一个int型变量x,&x表示取x的地址,将此值赋给p,那么p就是指针。取得指针指向的值,我们使用*p。示例如下:
var int x //x默认初始值为0 p := &x //p为整型指针,指向x fmt.Println(*p) //此时打印输出 0 *p = 1 //类似于x=1 fmt.Println(x) //此时打印输出1
说明
本书中默认初始值的叫法,就是Go官方文档中的Zero Value,很多文档和书本都将其直译为“零值”。不过本书沿用了C语言的叫法,都叫作默认初始值。
我们可以用指针来代替变量名,如果指针改变了变量值,会影响到变量名,因为本质上它们就是同一个内存空间。
上面的例子是基本类型。其实,复合类型(如结构体或数组)也是变量,它们也有一个地址,也是可以通过指针进行操作的。对应地,复合类型内的具体元素同样有地址,并且可以通过指针进行操作。
对于上例中的p,我们称之为指针类型。指针类型的默认初始值为nil,可以通过p==nil来判断是否取得地址,nil是未成功取得。
再来看一个对比:
book/chapter01/1.2/pointer/main.go
1. package main
2.
3. import "fmt"
4.
5. func main() {
6. m := 1
7. selfPlusPointer(&m)
8. fmt.Println(m) //2
9. fmt.Println(*selfPlus(1)) //2
10. }
11.
12. func selfPlusPointer(n *int){
13. *n++
14. }
15.
16. func selfPlus(n int) *int {
17. t := n+1
18. return &t
19. }
上述代码中有两个函数:selfPlusPointer和selfPlus,函数的具体介绍会在第4章进行,此处先大概解释一下。
第12行至第14行的selfPlusPointer函数非常简单,接收到的是一个int指针类型的参数,直接执行了自增运算。注意,自增在Go语言中不是一个表达式,不像C语言可以用n=n++,它是一个独立的语句,单独成行。这个函数没有返回值,因为我们直接通过指针把值修改了,相当于变量的值直接变了,也就不需要再传递了。
第16行至第19行的selfPlus函数接收到的是一个整型参数,这时候用一个临时变量暂时存储n的自增1结果,然后再返回临时变量的指针类型。
第5行至第10行是main函数。第6行定义变量m,要注意第7行&m参数的使用,它代表传递m的地址给selfPlusPointer函数。接着打印m,打印2。注意第9行的用法,因为selfPlus返回的是指针类型,所以我们打印的时候要以“*”为前缀,这样才是取得指针指向的值,如果不加“*”,打印的就是一个地址。
说明
在Go语言中,除非显式地使用指针,否则所有的值传递都是具体值的复制,包括数组等复合结构。所以,指针是Go语言工程师必须掌握的知识。
前面介绍指针的时候用的都是有变量名的变量,那么,可不可以直接用指针指向一个没有变量名的变量呢?
Go语言提供了new函数来帮助我们创建一个不需要名称的变量,并可以直接赋值给一个指针,其用法非常简单,示例如下:
p := new(int) //p为*int类型 fmt.Println(*p) //0
对于指针的介绍就到这里,请读者注意代码中对指针的使用,值得一提的是,Go语言的指针比C++语言的要安全得多。