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

2020-01-06 18:55:52 +08:00
 saltsugar

// 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]

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

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

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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/635569

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX