Golang 数组和切片深入分析
文章目录
一、数组
数组赋值给数组
Go 数组是值类型,因此赋值操作和函数传参数会复制整个数组的数据,例:
|
|
结果:
|
|
可以看到,b 的地址和 a 的地址不同,同时可以看到,数组的地址即为数组第一个元素的地址。
数组赋值给数组指针
上面已经看到数组直接赋值是值传递,可以考虑用指针来实现传地址,例:
|
|
结果:
|
|
可以看到,指针的地址和数组的地址不一样,指针的值是数组的地址,即这里指针 b 是一个指向数组 a 地址的变量。这样无论是直接赋值给指针,还是在函数中用指针来传递,都可以达到不复制数组,并且修改原数组值的目的。
二、切片
切片运行时实际结构为 SliceHeader ,其结构定义为:
|
|
三个成员即切片指向的数据源数组地址,切片长度和切片容量。当切片之间传递时,实际上是 SliceHeader 之间的值传递,由于传递之后,Data 指向的数组地址是同一个,所以修改操作会同步。下面从几个实际例子来探究不同情况下的运行结果。
从数组得到切片
1、切片得到数组时,如果切片没有进行扩容,则指向的数据源还是此数组,任何对数组或切片的值修改操作,另一个的值也随之改变
切片未扩容
Go 切片是可以从数组得到的,以下代码,从切片得到数组:
|
|
结果:
|
|
可以看到,切片 b 指向了新地址,但是第一个元素的地址和数组 a 的一致。那这里修改数组某个元素值切片的值会改变么,或者修改切片的某个元素值数组的值会改变么?下面继续测试:
|
|
结果:
|
|
可以看到,不管是修改数组的值,还是切片的值,另一个对应的值也改变了,这验证了切片指向的数据源是数组 a。
切片扩容情况
下面是从数组得到切片后,执行 append 操作:
|
|
结果:
|
|
可以看到,扩容后切片 b 第一个元素的地址发生了变化,因此后续对数组值得修改和对切片值的修改,都不会影响到另一个的值。
切片之间赋值
Golang 中切片是引用类型,直接赋值后,修改任意一个的某个元素值,另一个也会随之改变。但是如果在赋值之后,切片进行了扩容操作,则会指向新的数据源,因此修改值操作不会影响原来的切片。如果不想影响另一个的结果,可以用 copy 函数来实现。例:
|
|
结果:
|
|
切片 append 分析
当容量足够时,直接在当前长度的下一个位置保存数据,即使还有其他切片也指向这一块地址。例:
|
|
结果:
|
|
可以看到,由于多个切片指向同一块数据源,任何修改操作都会使得其他变量结果改变。
总结
对于切片,只要清楚它指向的数据源是否有改变,即关注 append 前后时,容量是否有变化,就能判断其运行结果。
文章作者 yefengzhichen
上次更新 2022-11-30