func main(){
aa := make([]string,0,8)
aa = append(aa,"aaa")
fmt.Printf("输出:%s,cap:%d\n",aa,cap(aa))
bb := aa
bb = append(bb,"bb","cc")
fmt.Printf("输出:%s,%p,%s,%p\n",aa,&aa,bb,&bb)
}
结果:
输出:[aaa],cap:8
输出:[aaa],0xc00000c030,[aaa bb cc],0xc00000c060
我在测试golang的切片共享内存地址的问题,比如我把aa赋值给bb,然后append bb,按理没有触发扩容,aa的切片打印内容和bb应该是一样的,应为共享底层的数组指针,但aa和bb打印的内容却不一样
golang切片地址共享问题
- 写回答
- 好问题 0 提建议
- 关注问题
- 邀请回答
-
2条回答 默认 最新
檀越@新空间 2025-11-25 18:35关注晚上好🌙🌙🌙
本答案参考通义千问你提到的这个问题是 Go 语言中 切片(slice)共享底层数组 的典型现象,但你的观察结果似乎有些矛盾。我们来详细分析一下。
一、问题分析
1. 初始代码如下:
func main() { aa := make([]string, 0, 8) aa = append(aa, "aaa") fmt.Printf("输出:%s,cap:%d\n", aa, cap(aa)) bb := aa bb = append(bb, "bb", "cc") fmt.Printf("输出:%s,%p,%s,%p\n", aa, &aa, bb, &bb) }2. 输出结果:
输出:[aaa],cap:8 输出:[aaa],0xc00000c030,[aaa bb cc],0xc00000c060
二、为什么
aa和bb的内容不一样?虽然
bb := aa是将aa的切片复制给bb,但此时aa和bb共享同一个底层数组。但是,在append(bb, "bb", "cc")这一步中,Go 会检查是否需要扩容,如果不需要扩容,则会直接修改底层数组,此时aa和bb都会看到这个变化。但是在你的测试中,
aa的内容没有被改变,这说明 发生了扩容!
三、关键点:切片的容量和扩容机制
1. 初始创建
aa:aa := make([]string, 0, 8)- 切片长度为 0,容量为 8。
- 底层数组大小为 8。
2. 第一次
append(aa, "aaa"):- 此时长度变为 1,容量仍为 8。
- 底层数组未扩容。
3.
bb := aa:bb指向与aa相同的底层数组。aa和bb共享同一个数组。
4.
bb = append(bb, "bb", "cc"):- 此时
bb的长度是 1 + 2 = 3,而容量是 8。 - 不会触发扩容,因为 3 < 8。
- 所以
bb的底层数组仍然和aa一致。
✅ 因此,理论上
aa和bb的内容应该是一样的。
四、为什么
aa的内容没变?根据你的输出:
输出:[aaa],0xc00000c030,[aaa bb cc],0xc00000c060aa的地址是0xc00000c030bb的地址是0xc00000c060
这意味着
bb不再指向原来的底层数组,而是重新分配了一个新的数组。这说明 在append(bb, "bb", "cc")时发生了扩容。
五、为什么会发生扩容?
1. 切片的扩容规则:
Go 中切片的扩容逻辑是这样的:
- 如果当前切片的容量足够容纳新元素,则不扩容;
- 否则,会分配一个 新数组,大小为原容量的 1.25 倍(或 2 倍,视情况而定),然后将旧数据拷贝过去。
2. 为什么
bb发生了扩容?bb的初始容量是 8。bb的长度是 1(因为aa的长度是 1)。append(bb, "bb", "cc")添加了两个元素,使长度变为 3。- 容量是 8,所以 不会扩容。
❌ 但你的测试中
bb的地址不同,说明扩容发生了。
六、可能原因:切片赋值后发生了不可见的变化
你可能误以为
bb := aa就是“引用传递”,但实际上:在 Go 中,切片是结构体,包含三个字段:
- 指针(指向底层数组)
- 长度
- 容量
当你执行
bb := aa,只是复制了这三个字段,并不意味着它们指向相同的底层数组(除非你显式地使用指针)。但在你的例子中,确实共享了同一个底层数组,但为何
bb的地址变了?
七、验证:打印底层数组地址
你可以通过以下方式查看底层数组的地址:
fmt.Printf("aa array address: %p\n", &aa[0]) fmt.Printf("bb array address: %p\n", &bb[0])
八、解决方案和解释
✅ 结论:
bb = append(bb, "bb", "cc")确实没有扩容,因为容量足够;- 但是,由于某些原因(如编译器优化或 GC 行为),
bb的地址改变了,这可能是 Go 的运行时行为,并非一定表示扩容。
📌 重点总结:
- 切片是结构体,包含指针、长度、容量;
- 赋值
bb := aa会共享底层数组; - 只有当
append触发扩容时,才会分配新数组; - Go 中的切片操作有时会因运行时行为导致看似“扩容”但实际未扩容。
九、修改后的代码(带底层数组地址打印)
package main import ( "fmt" ) func main() { aa := make([]string, 0, 8) aa = append(aa, "aaa") fmt.Printf("输出:%s,cap:%d\n", aa, cap(aa)) bb := aa bb = append(bb, "bb", "cc") fmt.Printf("输出:%s,%p,%s,%p\n", aa, &aa, bb, &bb) fmt.Printf("aa array address: %p\n", &aa[0]) fmt.Printf("bb array address: %p\n", &bb[0]) }🔁 输出示例:
输出:[aaa],cap:8 输出:[aaa],0xc00000c030,[aaa bb cc],0xc00000c060 aa array address: 0xc00000e0a0 bb array address: 0xc00000e0a0注意:如果你看到
aa和bb的底层数组地址相同,那么说明 确实没有扩容。
十、总结
| 问题 | 解决方案 | |------|----------| | 切片赋值后内容不一致 | 确保
append没有扩容,检查容量 | | 切片地址不同 | 可能是运行时行为,不是扩容 | | 如何验证底层数组 | 使用&slice[0]打印底层数组地址 |希望这个解释能帮助你理解 Go 中切片的内存共享机制!
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报