Go语言结构体中切片的内存存储及与指针切片的差异解析
Go结构体中切片与切片指针的内存解析
一、结构体字段为切片时的内存存储
Go语言里的切片本质是一个包含三个字段的内部结构体:
- 指向底层数组的指针
- 切片的长度(
len) - 切片的容量(
cap)
当你定义type deque struct { indexes []int }并初始化实例dq后,adr(dq)=0x1001这个地址对应的内存区域,存储的是整个deque结构体的内容。其中indexes字段占据的内存空间,就是上述切片的三个字段:
- 0x1001开始的前8字节(64位系统):存储指向底层数组的指针(比如你说的0x8009)
- 接下来8字节:存储切片当前的长度
- 再接下来8字节:存储切片当前的容量
它既不是直接存储数组本身,也不是只存一个数组指针,而是完整存储切片的三个核心属性。
二、执行d.indexes = append(d.indexes, i)的过程
append函数的执行逻辑分两种情况:
- 容量足够:如果当前切片的容量
cap大于等于len+1,直接在底层数组的len位置写入新元素i,然后将切片的长度len加1,返回原切片的引用,最后把d.indexes更新为这个长度增加后的切片。 - 容量不足:Go会自动分配一块新的、更大的内存作为新的底层数组(通常是原容量的2倍,小容量时可能按倍数增长,大容量时增长比例降低),把原底层数组的所有元素拷贝到新数组中,再写入新元素
i,然后返回一个指向新数组的新切片,最后将d.indexes替换为这个新切片。
因为d是指向deque实例的指针,所以对d.indexes的修改会直接作用于原结构体实例。
三、改为indexes *[]int的差异
当结构体定义为type deque struct { indexes *[]int }时,和原版本的核心差异如下:
- 内存存储不同:此时
adr(dq)=0x1001存储的deque结构体中,indexes字段是一个指针(8字节,64位系统),这个指针指向另一个内存地址(比如0x2000),而0x2000地址才是存储切片三个字段(数组指针、len、cap)的地方。 - 初始化与操作风险:使用时必须先初始化切片并将指针指向它,比如
dq.indexes = &[]int{},否则indexes是nil指针,直接调用append会触发panic。而原版本的切片字段默认是nil切片,可以直接调用append(会自动分配底层数组)。 - 拷贝与修改行为:
- 拷贝
deque实例时,原版本拷贝的是切片的三个字段,拷贝后的实例和原实例共享底层数组,但各自的切片字段是独立的(修改其中一个的len/cap不会影响另一个,除非修改底层数组元素); - 新版本拷贝的是切片指针,拷贝后的实例和原实例指向同一个切片结构体,修改任意一个的切片属性(len/cap、底层数组指针)都会直接影响另一个。
- 拷贝
- append的写法:新版本需要通过解引用指针来操作切片,比如
*dq.indexes = append(*dq.indexes, i),如果错误写成dq.indexes = &append(*dq.indexes, i),虽然语法合法,但完全没必要,还会额外创建切片指针的副本。
内容的提问来源于stack exchange,提问作者Tian Gao
相关产品推荐
相关产品推荐

