Go中的指针

学Java以来,让程序员忽略了指针和内存地址这些概念,Java帮我们封装了对象,简化了对象引用之间的关系。在Go语言中,又帮我们回忆起这些概念。

我们创建的每一个对象在内存中都有一个位置去存储,每个内存块都有一个地址表示当前位置,通常用十六进制表示,如0x24005676543。Go语言取地址的符号是&。放在一个变量前使用就会返回相应变量的内存地址。

比如下面的代码:

var a = 3
fmt.Printf("num is : %d, it's location in memory: %p\n", a, &a)
输出:

num is : 3, it's location in memory: 0xc000062080

可以看到&a输出了a的内存地址。

你可以将&a赋值给一个变量,然后观察这个变量的类型,使用reflect.TypeOf()方法:

var a = 3
intP = &a
b := &a
fmt.Println(reflect.TypeOf(b))

输出:
*int

可以看到&a的返回类型是 *int。

我们可以将一个变量的内存地址放在一个叫做指针的特殊数据类型中,声明一个指针的方式如下:

var p *int

然后我们可以使用p去指向一个内存地址:

p = &a

那么我们如何通过指针来获取对应内存地址的值呢,这也是有办法的。你可以在指针类型前面加上 * 号(前缀)来获取指针所指向的内容,这里的 * 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。

var p *int
var a = 3
p = &a
fmt.Println(*p)

*p打印的是a的值。

当一个指针被定义没有分配任何变量的时候,它的值为nil。

对于任何一个变量val,如下表达式都是正确的:

var == *(&var)

有如下你需要注意的是,你不能获取到一个常量或者一个没有赋值操作的文字的内存地址:

const PI  = 3.14
piP := &PI Cannot take the address of 'PI'
p2 := &3 Cannot take the address of '3'

在c语言中,指针是可以由程序员自行控制移动位置的,这无疑给程序增加了很多的不确定性,在Go中指针的概念其实更相当于是Java中的引用。它比Java中的引用高级之处在于:

Java中的参数传递,传递的是值的拷贝,但是在Go中你可以使用指针来传递这个值,它直接指向了这个值的内存地址,并且只占用了4个或者8个字节。

Go 默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过程中可能对副本的值进行更改,但不会影响到原来的变量 。如果你希望函数可以直接修改参数的值,而不是对参数的副本进行操作,你需要将参数的地址(变量名前面添加&符号,比如 &variable)传递给函数,这就是按引用传递 。此时传递给函数的是一个指针 。