Go中打印变量为何会引发栈溢出?
Go中append操作引发栈溢出的原因
先看触发栈溢出的复现代码:
package main import "fmt" func main() { values := []any{1,2,3} values = append(values[:1],values[1:]) fmt.Println(values) }
现象整理
- 代码执行时会触发栈溢出,且确定是
fmt.Println(values)这一行导致的(之前的语句能正常打印提示信息) - 若把打印的
values换成其他内容(比如0),程序无报错 - 改用
append(values[:1], values[1:]...)(用...展开可变参数),代码正常输出[1 2 3] - 单独打印
values[0]没问题,但打印values[1]会直接溢出 - 对比以下正常运行的代码:
package main import "fmt" func main() { values := append([]any{1},[]any{2,3}) fmt.Println(values) }
问题根源
核心问题出在append的参数传递方式和切片底层数组共享导致的循环引用:
append参数类型不匹配:Go的
append要求第二个及以后的参数是切片元素类型的值,或者用...展开同类型切片。错误代码里直接传values[1:](一个[]any),没有用...,相当于把整个切片作为单个元素添加到values[:1]里。最终values的结构是[]any{1, []any{2, 3}}。循环引用导致无限递归:错误代码里的
values[1:]是原切片的子切片,和原切片共享同一个底层数组。当这个子切片被作为元素添加到外层切片后,就形成了循环引用:外层切片的底层数组包含了子切片,而子切片又指向这个底层数组。fmt.Println打印切片时会递归遍历每个元素,遇到子切片就会继续打印它的元素,无限递归下去,最终耗尽栈空间引发溢出。
而改用...展开参数时,append会把values[1:]里的每个元素逐个添加,最终得到正常的[]any{1,2,3},没有循环引用,自然能正常打印。
至于那个正常运行的对比代码,虽然同样是把一个切片作为单个元素添加,但[]any{2,3}是全新创建的切片,和外层切片[]any{1}的底层数组不共享,所以打印时递归到子切片就结束了,不会无限循环。
内容的提问来源于stack exchange,提问作者Flummox
相关产品推荐
相关产品推荐

