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

go slice defaults 这里是坑还是如此设计?

  •  
  •   saltsugar · 2020-01-06 18:55:52 +08:00 · 3107 次点击
    这是一个创建于 1779 天前的主题,其中的信息可能已经有所发展或是发生改变。

    // https://tour.golang.org/moretypes/10

    package main

    import "fmt"

    func main() { s := []int{1, 2, 3, 4, 5, 6}

    s = s[1:4]
    fmt.Println(s)
    
    s = s[:5]
    fmt.Println(s) // 这里 s 接上面的起点 2,但是终点却是 6 ?
    
    s = s[1:]
    fmt.Println(s) 
    

    }

    ////output

    [2 3 4]

    [2 3 4 5 6]

    [3 4 5 6]

    14 条回复    2020-01-10 01:32:59 +08:00
    doylecnn
        1
    doylecnn  
       2020-01-06 19:11:29 +08:00 via Android
    下标从 0 开始呀,哪里坑了?
    catror
        2
    catror  
       2020-01-06 19:12:27 +08:00 via Android
    这是设计,边界不超过 cap 就行
    ArJun
        3
    ArJun  
       2020-01-06 19:15:02 +08:00
    这种赋值操作一般不推荐吧,你使用的始终是一个对象
    lostpg
        4
    lostpg  
       2020-01-06 19:23:34 +08:00 via Android
    下表从零开始,end 的值是末尾元素下标+1。这种用法有梗的
    lostpg
        5
    lostpg  
       2020-01-06 19:24:45 +08:00 via Android
    @lostpg 看岔了,忽略
    petelin
        6
    petelin  
       2020-01-06 19:24:54 +08:00 via iPhone
    第二个用法我其实挺惊讶的
    我一直以为边界是 len 这样看是 cap
    saltsugar
        7
    saltsugar  
    OP
       2020-01-06 19:42:31 +08:00
    @doylecnn 是下标结尾哈

    @catror 直觉上理解,slice 前后边界应该是限定了。
    比如第三次 slice 的首边界就在原来基础上+1 了。
    如果第二次 slice 能将上一个尾部扩展的话,那么首边界却不能往前移动,这点反直觉吧

    @ArJun 是不推荐。反直觉啊
    @petelin
    ethego
        8
    ethego  
       2020-01-06 19:53:32 +08:00
    数组越界了,所以取到了 slice 外面的东西,这时候是未定义行为,取到任何东西都不奇怪。只是这里 slice 没有发生拷贝,取到了原地址后面的东西了。
    ethego
        9
    ethego  
       2020-01-06 20:06:18 +08:00
    至于取分片为什么没有拷贝或者不做越界检查当然是为了性能,所以挺合理的
    zhyl
        10
    zhyl  
       2020-01-07 10:19:28 +08:00
    切片和数组是不同的. 两种类型能够索引数据的范围都是在 len(x)之内的.
    但是切片可以通过重新切片来扩展它的 len, 重新切片允许的范围是其底层数组决定的, 它等于切片的 cap.
    不存在数组越界和未定义行为这种说法.
    ethego
        11
    ethego  
       2020-01-07 11:26:02 +08:00
    @zhyl 嗯,试了下你说的是对的
    zhuyuefeng
        12
    zhuyuefeng  
       2020-01-08 23:24:33 +08:00
    这么解释不知道合不合适:


    // origin
    s := []int{1, 2, 3, 4, 5, 6}

    s = s[1:4]
    // 此时 s 是原数组的切片,其内容就是[2 3 4],索引 index 1, 2, 3 的值
    // 但原数组的 cap 比现在的切片大,所以目前是可以扩展的
    // [2 3 4]
    fmt.Println(s)


    s = s[:5]
    // 进行重切片,此时发现原切片不够大,但底层数组的 cap 够,也就增加了后面的 6,就是 index=4 的值
    fmt.Println(s)
    // [2 3 4 5 6]


    s = s[1:]
    // 此时又重新切片 去掉第一个元素 index=0,也就是 2,那么返回的数据就是[3 4 5 6]
    fmt.Println(s)
    // [3 4 5 6]
    saltsugar
        13
    saltsugar  
    OP
       2020-01-10 01:31:38 +08:00
    谢谢楼上各位的讨论和回答。总结一下

    package main

    import (
    "fmt"
    )

    func main() {
    fmt.Println("Hello, playground")

    a := []int{1,2,3,4,5,6}
    printSlice(a)

    b := a[1:3]
    b[0] = 0
    printSlice(b)

    c := b[:3]
    c[0] = 0
    printSlice(c)

    //d := c[-1:] 可惜不能往前扩
    //printSlice(d)

    d := c[1:4]
    d[0] = 0
    printSlice(d)

    e := d[1:4] //通过小切片来移动窗口处理数据还是挺方便的。
    e[0]=0
    printSlice(e)

    printSlice(a)
    }

    func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
    }


    Hello, playground
    len=6 cap=6 [1 2 3 4 5 6]
    len=2 cap=5 [0 3]
    len=3 cap=5 [0 3 4]
    len=3 cap=4 [0 4 5]
    len=3 cap=3 [0 5 6]
    len=6 cap=6 [1 0 0 0 5 6]
    saltsugar
        14
    saltsugar  
    OP
       2020-01-10 01:32:59 +08:00
    另外,append 后,变量指向新数组实例了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1349 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:42 · PVG 01:42 · LAX 09:42 · JFK 12:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.