分析go中slice的奇怪现象

栏目: Go · 发布时间: 6年前

内容简介:片段一:片段二:可以看到,片段一和片段二中s1和s2输出不一致。

片段一:

s := []byte("")
s1 := append(s,'a')
s2 := append(s,'b')
fmt.Println(s1,"=====",s2) // [97] ===== [98]
fmt.Println(string(s1),"======",string(s2)) // a ====== b
复制代码

片段二:

s := []byte("")
s1 := append(s,'a')
s2 := append(s,'b')
fmt.Println(string(s1),"======",string(s2)) // b ====== b
复制代码

可以看到,片段一和片段二中s1和s2输出不一致。

问题分析

初看起来,感觉是 fmt.Println(s1,"=====",s2) 这句话导致了结果的不一样。

具体原因,且看下面分解。

对于片段二,结果都是 b ,这个似乎是因为 append 的时候,赋值给了一个新的变量,导致了s指向的底层数据虽然改变了,但是s记录的len还是不变的,所以第二次append的时候,就把第一次的值给覆盖了。所以才得出了都是 b 的结果。

对于问题二的解释,其实还少考虑了一个问题,那就是如果在append的时候,底层数组重新分配了,那么,就不会出现这个问题了。不信,看下面这个例子:

s := []int{5, 7, 9}
fmt.Printf("%d, %d, %p\n", len(s), cap(s), &s[0]) // 3, 3, 0xc000016240
x := append(s, 11)
fmt.Printf("%d, %d, %p\n", len(x), cap(x), &x[0]) // 4, 6, 0xc0000141e0
y := append(s, 12)
fmt.Printf("%d, %d, %p\n", len(y), cap(y), &y[0]) // 4, 6, 0xc000014210
fmt.Println(s, x, y) // [5 7 9] [5 7 9 11] [5 7 9 12]
复制代码

从上面例子可以看到,一开始s的len是3, cap也是3,在第一次append的时候,底层数组需要扩容,翻倍为6,所以x的len是4, cap是6,此时指向的底层数组地址已经不同了,第二个append同理。所以两次append,因为底层数据扩容的原因,两次append作用的地方是不同的,导致第二次没有覆盖第一次的数据。

通过上面的解释,我们知道了第一个片段出现的原因就是因为两次append都扩容了,所以没有出现覆盖的现象。而第二个片段没有出现扩容的情况,所以就出现了覆盖的情况。

那么为什么会这样呢,我们尝试打印一下,初始化s的cap,可以发现片段一中初始化s的cap为0,片段二为32。具体原因可以看下面的逃逸分析

逃逸分析

使用下面的命令进行分析。

go tool compile -m main.go
复制代码

片段一:

main.go:9:13: s1 escapes to heap
main.go:6:13: ([]byte)("") escapes to heap
main.go:9:17: "=====" escapes to heap
main.go:9:17: s2 escapes to heap
main.go:10:20: string(s1) escapes to heap
main.go:10:20: string(s1) escapes to heap
main.go:10:25: "======" escapes to heap
main.go:10:40: string(s2) escapes to heap
main.go:10:40: string(s2) escapes to heap
main.go:9:13: main ... argument does not escape
main.go:10:13: main ... argument does not escape
复制代码

可以看到 s1s2 由于Println,逃逸到了heap上,所以s一开始的cap是0.

片段二:

main.go:9:20: string(s1) escapes to heap
main.go:9:20: string(s1) escapes to heap
main.go:9:25: "======" escapes to heap
main.go:9:40: string(s2) escapes to heap
main.go:9:40: string(s2) escapes to heap
main.go:6:13: main ([]byte)("") does not escape
main.go:9:13: main ... argument does not escape
复制代码

可以看到 s1s2 没有逃逸,还是在栈上,所以s一开始的cap是32(默认).


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Ruby

Programming Ruby

Dave Thomas、Chad Fowler、Andy Hunt / Pragmatic Bookshelf / 2004-10-8 / USD 44.95

Ruby is an increasingly popular, fully object-oriented dynamic programming language, hailed by many practitioners as the finest and most useful language available today. When Ruby first burst onto the......一起来看看 《Programming Ruby》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具