分析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(默认).


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

查看所有标签

猜你喜欢:

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

数据化运营速成手册

数据化运营速成手册

胡晨川 / 电子工业出版社 / 2017-4 / 55

《数据化运营速成手册》用于提升互联网公司员工的数据应用能力,即数据化运营能力。首先,从最常用的数据图表切入,帮助执行层正确地绘图,管理层正确地看图;接着,梳理运营中最基本的数据应用知识,涉及数据获取、数据清洗、数据认知、分析框架、指标体系、运营实验等内容。然后,介绍作者认为必要的统计学知识,包括假设检验、方差分析、回归分析和时间序列分解,并引入了管理科学中的规划求解方法。最后,介绍了数据分析工具的......一起来看看 《数据化运营速成手册》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

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

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具