Go语言切片传参结果不符预期:为何输出{0x03,0x02}?
要搞懂这个问题,得先理清Go里切片的本质和函数参数传递的规则——Go里所有参数传递都是值传递,切片也不例外,但切片本身是一个包含三个核心字段的结构体,这是问题的关键所在。
先拆解切片的内部结构
每个切片在Go内部都对应这样一个隐形的结构体:
type slice struct { ptr *[]byte // 指向底层数组的指针 len int // 当前切片的可用长度 cap int // 底层数组的总容量 }
当你把切片作为参数传给函数时,传递的是这个结构体的完整副本,而非底层数组本身。
一步步分析你的代码执行流程
main函数初始化切片a:
a := []byte{0x01, 0x02}此时a的结构体状态是:
ptr指向底层数组[0x01, 0x02],len=2,cap=2。调用test1(a),传递切片副本:
函数参数t拿到的是a的切片结构体副本,所以t.ptr和a.ptr指向同一个底层数组,t.len和t.cap也都是2。执行
t[0] = 0x03:
因为t.ptr和a.ptr指向同一个底层数组,修改t[0]本质是直接修改底层数组的第一个元素,所以a的底层数组会同步变成[0x03, 0x02]。执行
t = append(t, 0x01):
此时t.len=2,t.cap=2,append需要添加一个元素,但容量已经耗尽。Go会自动分配一个新的底层数组(通常是原容量的2倍,这里就是4),把原底层数组的元素[0x03, 0x02]拷贝过去,再添加新元素0x01,新的底层数组变成[0x03, 0x02, 0x01, 0x00](末尾的0是默认填充值)。
这时候,t的结构体被更新:ptr指向这个新的底层数组,len=3,cap=4。但这一切都和a无关——a的结构体还是原来的状态,ptr依然指向旧的底层数组,len和cap还是2。
最终结果
main函数里的a,它的底层数组是[0x03, 0x02],长度是2,所以输出就是{0x03, 0x02}。
补充:如果想让append的结果被main看到怎么办?
有两种常用方式:
- 让test1函数返回修改后的切片,main里重新赋值给a:
func test1(t []byte) []byte { t[0] = 0x03 t = append(t, 0x01 ) return t } // main里 a = test1(a) - 传递切片的指针给函数:
func test1(t *[]byte) { (*t)[0] = 0x03 *t = append(*t, 0x01 ) } // main里 test1(&a)
内容的提问来源于stack exchange,提问作者P.An

