golang 切片append问题

wangam · 2020-06-28 15:05:33 · 2056 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2020-06-28 15:05:33 的主题,其中的信息可能已经有所发展或是发生改变。

image.png

有个问题需要大神帮忙解决一下

  • 1,当对s2进行append数据时,为什么s1没有改变
  • 2,当对s1进行append追加s2数据时,为什么会改变s2的值

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2056 次点击  
加入收藏 微博
11 回复  |  直到 2020-07-02 16:43:05
focusonline
focusonline · #1 · 5年之前

这样的结果的确令人困惑, 能否认为这是一个golang的bug? 改成如下的代码结果会让人更加迷惑.

package main

import "fmt"

func main() {
   fmt.Println("Hello, World!")
    s1 := make([]int, 3, 10)
    s2 := s1
    s2[0] = 100
    fmt.Println(s1)
    fmt.Println(s2)
    s2 = append(s2, 1, 2, 3, 4)
    fmt.Println(s1, cap(s1), s2, cap(s2))
    s1 = append(s1, s2...)
    fmt.Println(s1, cap(s1), s2, cap(s2))
    s2 = append(s2, 1000)
    fmt.Println(s1, cap(s1), s2, cap(s2))
}

结果如下:
Hello, World!
[100 0 0]
[100 0 0]
[100 0 0] 10 [100 0 0 1 2 3 4] 10
[100 0 0 100 0 0 1 2 3 4] 10 [100 0 0 100 0 0 1] 10
[100 0 0 100 0 0 1 1000 3 4] 10 [100 0 0 100 0 0 1 1000] 10
jarlyyn
jarlyyn · #2 · 5年之前

slice是一段内存地址。

然后有对应有效位数和最大长度(cap)

s1和s2是应对到同一段内存地址,有不同的有效位数的指。

append操作既该表 内存地址中的值,又改变了有效位数。

要避免这个问题,你需要不用使用s2:=s1这样含义不明的操作。

要复制Slice的话,使用make然后copy。

jarlyyn
jarlyyn · #3 · 5年之前

如果要复制slice,用make+copy

要操作slice,用指针

jarlyyn
jarlyyn · #4 · 5年之前

再回到题目问题

改变s2的值的时候,s1其实也是改变的,只是长度不到,所以你没看到改变的部分。

focusonline
focusonline · #5 · 5年之前
如果归根结底到长度上,那应该就解释的通了.slice的实现就是复制时,
如果不超过容量那么底层数组数据共享, 但是长度数据或者说有效位数是不共享的, 各玩各的.
如果超过容量那么底层数组数据复制一个全新的2倍容量, 使用一个全新的和原先没有任何关系的新的slice
所以就出现了上述奇怪的问题了.
wangam
wangam · #6 · 5年之前
jarlyynjarlyyn #4 回复

再回到题目问题 改变s2的值的时候,s1其实也是改变的,只是长度不到,所以你没看到改变的部分。

这里的长度不到是指什么,怎么能看到所谓的长度

jarlyyn
jarlyyn · #7 · 5年之前
wangamwangam #6 回复

#4楼 @jarlyyn 这里的长度不到是指什么,怎么能看到所谓的长度

长度是len,容量是cap啊

yc8332
yc8332 · #8 · 5年之前

s1,s2底层就是同一块内存,如果是要表示不同的内容,要引入copy,copy一份新的出来

yuantiancai
yuantiancai · #9 · 5年之前

s2 := s1, s2和s1都是[0,0,0],并指向同一底层数组,底层数组是[0,0,0,0,0,0,0,0,0,0],

s2[0] = 100, s2和s1都是[100,0,0],底层数组[100,0,0,0,0,0,0,0,0,0],

s2 = append(s2, 1, 2, 3, 4), s1是[100,0,0], s2是[100,0,0,1,2,3,4], 底层数组是[100,0,0,1,2,3,4,0,0,0],

s1 = append(s1, s2...), s1是[100,0,0,100,0,0,1,2,3,4], s2是[100,0,0,100,0,0,1], 底层数组[100,0,0,100,0,0,1,2,3,4],

切片是对底层数组某一段的截取,切片的改变会影响它在底层数组对应的那一段

yuantiancai
yuantiancai · #10 · 5年之前
focusonlinefocusonline #1 回复

这样的结果的确令人困惑, 能否认为这是一个golang的bug? 改成如下的代码结果会让人更加迷惑. ```go package main import "fmt" func main() { fmt.Println("Hello, World!") s1 := make([]int, 3, 10) s2 := s1 s2[0] = 100 fmt.Println(s1) fmt.Println(s2) s2 = append(s2, 1, 2, 3, 4) fmt.Println(s1, cap(s1), s2, cap(s2)) s1 = append(s1, s2...) fmt.Println(s1, cap(s1), s2, cap(s2)) s2 = append(s2, 1000) fmt.Println(s1, cap(s1), s2, cap(s2)) } 结果如下: Hello, World! [100 0 0] [100 0 0] [100 0 0] 10 [100 0 0 1 2 3 4] 10 [100 0 0 100 0 0 1 2 3 4] 10 [100 0 0 100 0 0 1] 10 [100 0 0 100 0 0 1 1000 3 4] 10 [100 0 0 100 0 0 1 1000] 10 ```

不是bug,只是你没理解而已,还有,认真解决问题,不要搞笑

gopher_liu
gopher_liu · #11 · 5年之前

我的理解是这样 如果append操作超出当前slice容量,会引发扩容,一旦扩容,则slice会另外分配一块内存地址去存放扩容后的数据。所以当前的修改操作已经和原来数组脱离关系了,可以试着打印出扩容前s1和s2首元素的地址。append引发扩容后再一次打印s1,s2首元素地址,会发现扩容后s1和s2数组首元素的地址已经不一样了

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传