V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
lysShub
V2EX  ›  Go 编程语言

b = append(b[:1], b...) 为什么能右移?

  •  
  •   lysShub · 180 天前 · 1833 次点击
    这是一个创建于 180 天前的主题,其中的信息可能已经有所发展或是发生改变。
    func TestXxxx(t *testing.T) {
    	var b = make([]byte, 128, 512)
    	rand.Read(b)
    
    	b1 := slices.Clone(b)
    	b = append(b[:1], b...)
    	b2 := b[1:]
    
    	require.Equal(t, b1, b2)
    
    	ptr := uintptr(unsafe.Pointer(unsafe.SliceData(b)))
    	ptr2 := uintptr(unsafe.Pointer(unsafe.SliceData(b2)))
    	require.Equal(t, ptr+1, ptr2)
    }
    

    第一步:把 b[0]放入 b[1], 这没问题

    第二步:把 b[1]放入 b[2], 可是这是的 b[1]在第一步被覆盖了,这样推导下去,整个 b[i]都会变为 b[0] ?

    10 条回复    2025-03-06 22:08:34 +08:00
    MoYi123
        1
    MoYi123  
       180 天前
    我觉得大概率是编译成 memmove 了.
    lysShub
        2
    lysShub  
    OP
       180 天前
    @MoYi123 看了下汇编,确实有 memmove
    my3157
        3
    my3157  
       180 天前
    和前面的没啥关系, b 和 b2 本来就是引用了同一个底层数组的不同位置而已

    b = append(b[:1], b...)
    b2 := b[1:]
    lasuar
        4
    lasuar  
       180 天前
    不明白为什么产生问题。。
    kandaakihito
        5
    kandaakihito  
       180 天前
    额。。。?我是建议楼主先去看看 slice 的结构体相关的八股文,看完就理解了。

    简单来说,此时 b 和 b2 指向的切片内存地址是同一块,你这上面的操作全都在同一段地址上进行。
    ns09005264
        6
    ns09005264  
       180 天前
    只是把数组 b 的第一个元素移动到最后,完了以后在数组 b 上新创建了切片 b2 ,只比数组 b 少了第一个元素。
    后面用 SliceData 获取数组底层的第一个元素指针,数组 b 的指针+1 后就跑到底层的第二个元素指针,切片 b2 的第一个元素指针就是数组 b 的第二个元素。
    lysShub
        7
    lysShub  
    OP
       180 天前
    楼上几个题也不读,正是因为 slice 的数据是指针引用才会有这个问题
    lysShub
        8
    lysShub  
    OP
       180 天前
    实际证明,append(v, slice...) 使用 memmv 不仅是出于性能, 为了正确性也必须这么做。

    如果把它编译为 for 循环结果就是错误的
    func TestZzzzz(t *testing.T) {
    var b = make([]byte, 128, 512)
    rand.Read(b)

    b1 := slices.Clone(b)

    b2 := b[:1]
    { // append
    for _, e := range b {
    b2 = append(b2, e)
    }
    }
    b2 = b2[1:]

    require.Equal(t, b1, b2)
    }
    my3157
        9
    my3157  
       180 天前   ❤️ 1
    @lysShub #8 根本不是一个问题啊

    后面这个还是共享底层数组的问题, b2 := b[:1] 只是对 b 底层数组的引用, 后面循环修改 b2 相当于修改 b
    ns09005264
        10
    ns09005264  
       180 天前
    你的疑惑似乎都是切片的 append 带来的,可以找找切片的详细资料看看。
    ```
    var b = make([]byte, 10, 10)
    b2 := b[1:2] // 新切片从 b 的位置 1 开始,到 1 结束,长度只有 1, 但是容量是 b 的容量减开始的位置 1 等于 9
    fmt.Println("b: ", b) // [0 0 0 0 0 0 0 0 0 0]
    // b2 追加新元素,但容量是 10-1=9 ,长度是 1, 不触发扩容,改变了原切片 b 的第三元素
    b2 = append(b2, 7)
    fmt.Println("b: ", b) // [0 0 7 0 0 0 0 0 0 0]
    // b3 容量是 1,长度是 1, 改变 b3 第一个元素会影响原切片 b 。
    b3 := b[9:]
    b3[0] = 6
    b3 = append(b3, 8)
    fmt.Println("b: ", b) // [0 0 7 0 0 0 0 0 0 6]
    // 由于 b3 上面追加了新元素,触发扩容,b3 开辟了新空间,和原切片没有关系了,改变第一个元素不影响原切片
    b3[0] = 5
    fmt.Println("b: ", b) // [0 0 7 0 0 0 0 0 0 6]
    ```
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3728 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 00:49 · PVG 08:49 · LAX 17:49 · JFK 20:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.