本文参考:

我们直接进入正题

Golang的指针其实与C和C++几乎一致,只是slice把数组指针封装了,刚开始容易搞混。接下来我们讨论golang中slice与二级指针的一些小坑。

slice一般不使用指针,因为slice本身就是引用类型,它本身就是一个指针,直接传递就可以修改其内部包含的数据。但其本质是一个结构体,其第一个字段是指向数据的指针,第二个字段是长度,第三个字段是容量。使用 slice 指针可以用来修改指针变量指向的slice。

比如:

所以

此时,a是一个slice,对于真实存储的数组相当于一个一级指针。

此时,a是一个slice的指针(*[]int),那么,相对于真实存储的数组,就相当于一个二级指针。

那么我们来看下一个例子:

而在下面的情况就不同了

在了解前面两个问题之后,接下来我们来看终极版(本质上和前面的知识点相同):

如果不清楚的话,我们来把流程走一遍:

我们创造了一个[0,1,2,3]数组,假设它的地址为0x11111111。

a是一个slice,a记录了0x11111111这个地址,相当于一级指针,我们可以用(*0x11111111)[0..n]来访问或者修改index为0到n的数组值。

此时我们调用函数FuncSlice(a, 4),把a传进去了,这个时候其实我们是传了0x11111111这个值的copy。在FuncSlice外部,a记录的还是0x11111111这个地址。

那么在FuncSlice函数内,我们知道了0x11111111这个值,也就相当于知道了原本的数组元素的地址,并且可以通过(*0x11111111)[0..n]来访问或者修改index为0到n的值。

所以,此时在函数内部的本地变量s记录的地址为0x11111111,所以s[0]++相当于

(*0x11111111)[0]++,这个时候数组本身的值被改变了。所以这个时候数组为[1,1,2,3]

下一步非常关键:

注意:append只有在容量不够的时候才会分配新的数组,例子中数组的cap明显不足,所以,这里是在底层产生了一个新的数组[1,1,2,3,4](感谢评论区的提醒,在此解释一下,以免误导大家)。

所以,他是一个新的数组,所以地址肯定不同,我们可以假设为0x22222222。所以此时s这个本地变量记录的地址为0x22222222。那么之后所有的操作,完全是对0x22222222这个地址的数组操作了。而在0x11111111处的数组依然为[1,1,2,3]。

而我们说过在FuncSlice外部,a记录的还是0x11111111这个地址。所以用a打印出来的数组当然还是[1,1,2,3]了。


希望能和对区块链感兴趣的朋友多多交流,我的blog地址: