千家信息网

切片传递的隐藏危机有哪些

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章主要讲解了"切片传递的隐藏危机有哪些",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"切片传递的隐藏危机有哪些"吧!在Go的源码库或者其他开源项目
千家信息网最后更新 2026年10月31日切片传递的隐藏危机有哪些

这篇文章主要讲解了"切片传递的隐藏危机有哪些",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"切片传递的隐藏危机有哪些"吧!

在Go的源码库或者其他开源项目中,会发现有些函数在需要用到切片入参时,它采用是指向切片类型的指针,而非切片类型。这里未免会产生疑问:切片底层不就是指针指向底层数组数据吗,为何不直接传递切片,两者有什么区别?

例如,在源码log包中,Logger对象上绑定了formatHeader方法,它的入参对象buf,其类型是*[]byte,而非[]byte。

1func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {}

有以下例子

 1func modifySlice(innerSlice []string) {
2 innerSlice[0] = "b"
3 innerSlice[1] = "b"
4 fmt.Println(innerSlice)
5}
6
7func main() {
8 outerSlice := []string{"a", "a"}
9 modifySlice(outerSlice)
10 fmt.Print(outerSlice)
11}
12
13// 输出如下
14[b b]
15[b b]

我们将modifySlice函数的入参类型改为指向切片的指针

 1func modifySlice(innerSlice *[]string) {
2 (*innerSlice)[0] = "b"
3 (*innerSlice)[1] = "b"
4 fmt.Println(*innerSlice)
5}
6
7func main() {
8 outerSlice := []string{"a", "a"}
9 modifySlice(&outerSlice)
10 fmt.Print(outerSlice)
11}
12
13// 输出如下
14[b b]
15[b b]

在上面的例子中,两种函数传参类型得到的结果都一样,似乎没发现有什么区别。通过指针传递它看起来毫无用处,而且无论如何切片都是通过引用传递的,在两种情况下切片内容都得到了修改。

这印证了我们一贯的认知:函数内对切片的修改,将会影响到函数外的切片。但,真的是如此吗?


考证与解释


在《你真的懂string与[]byte的转换了吗》一文中,我们讲过切片的底层结构如下所示。

1type slice struct {
2 array unsafe.Pointer
3 len int
4 cap int
5}

array是底层数组的指针,len表示长度,cap表示容量。

我们对上文中的例子,做以下细微的改动。

 1func modifySlice(innerSlice []string) {
2 innerSlice = append(innerSlice, "a")
3 innerSlice[0] = "b"
4 innerSlice[1] = "b"
5 fmt.Println(innerSlice)
6}
7
8func main() {
9 outerSlice := []string{"a", "a"}
10 modifySlice(outerSlice)
11 fmt.Print(outerSlice)
12}
13
14// 输出如下
15[b b a]
16[a a]

神奇的事情发生了,函数内对切片的修改竟然没能对外部切片造成影响?

为了清晰地明白发生了什么,将打印添加更多细节。

 1func modifySlice(innerSlice []string) {
2 fmt.Printf("%p %v %p\n", &innerSlice, innerSlice, &innerSlice[0])
3 innerSlice = append(innerSlice, "a")
4 innerSlice[0] = "b"
5 innerSlice[1] = "b"
6 fmt.Printf("%p %v %p\n", &innerSlice, innerSlice, &innerSlice[0])
7}
8
9func main() {
10 outerSlice := []string{"a", "a"}
11 fmt.Printf("%p %v %p\n", &outerSlice, outerSlice, &outerSlice[0])
12 modifySlice(outerSlice)
13 fmt.Printf("%p %v %p\n", &outerSlice, outerSlice, &outerSlice[0])
14}
15
16// 输出如下
170xc00000c060 [a a] 0xc00000c080
180xc00000c0c0 [a a] 0xc00000c080
190xc00000c0c0 [b b a] 0xc000022080
200xc00000c060 [a a] 0xc00000c080

在Go函数中,函数的参数传递均是值传递。那么,将切片通过参数传递给函数,其实质是复制了slice结构体对象,两个slice结构体的字段值均相等。正常情况下,由于函数内slice结构体的array和函数外slice结构体的array指向的是同一底层数组,所以当对底层数组中的数据做修改时,两者均会受到影响。

但是存在这样的问题:如果指向底层数组的指针被覆盖或者修改(copy、重分配、append触发扩容),此时函数内部对数据的修改将不再影响到外部的切片,代表长度的len和容量cap也均不会被修改。

为了让读者更清晰的认识到这一点,将上述过程可视化如下。

可以看到,当切片的长度和容量相等时,发生append,就会触发切片的扩容。扩容时,会新建一个底层数组,将原有数组中的数据拷贝至新数组,追加的数据也会被置于新数组中。切片的array指针指向新底层数组。所以,函数内切片与函数外切片的关联已经彻底斩断,它的改变对函数外切片已经没有任何影响了。

注意,切片扩容并不总是等倍扩容。为了避免读者产生误解,这里对切片扩容原则简单说明一下(源码位于src/runtime/slice.go 中的 growslice 函数):

切片扩容时,当需要的容量超过原切片容量的两倍时,会直接使用需要的容量作为新容量。否则,当原切片长度小于1024时,新切片的容量会直接翻倍。而当原切片的容量大于等于1024时,会反复地增加25%,直到新容量超过所需要的容量。

到此,我们终于知道为什么有些函数在用到切片入参时,它需要采用指向切片类型的指针,而非切片类型。

 1func modifySlice(innerSlice *[]string) {
2 *innerSlice = append(*innerSlice, "a")
3 (*innerSlice)[0] = "b"
4 (*innerSlice)[1] = "b"
5 fmt.Println(*innerSlice)
6}
7
8func main() {
9 outerSlice := []string{"a", "a"}
10 modifySlice(&outerSlice)
11 fmt.Print(outerSlice)
12}
13
14// 输出如下
15[b b a]
16[b b a]

请记住,如果你只想修改切片中元素的值,而不会更改切片的容量与指向,则可以按值传递切片,否则你应该考虑按指针传递。


例题巩固

为了判断读者是否已经真正理解上述问题,我将上面的例子做了两个变体,读者朋友们可以自测。

测试一

 1func modifySlice(innerSlice []string) {
2 innerSlice[0] = "b"
3 innerSlice = append(innerSlice, "a")
4 innerSlice[1] = "b"
5 fmt.Println(innerSlice)
6}
7
8func main() {
9 outerSlice := []string{"a", "a"}
10 modifySlice(outerSlice)
11 fmt.Println(outerSlice)
12}

测试二

 1func modifySlice(innerSlice []string) {
2 innerSlice = append(innerSlice, "a")
3 innerSlice[0] = "b"
4 innerSlice[1] = "b"
5 fmt.Println(innerSlice)
6}
7
8func main() {
9 outerSlice:= make([]string, 0, 3)
10 outerSlice = append(outerSlice, "a", "a")
11 modifySlice(outerSlice)
12 fmt.Println(outerSlice)
13}

测试一答案

1[b b a]
2[b a]

测试二答案

1[b b a]
2[b b]

感谢各位的阅读,以上就是"切片传递的隐藏危机有哪些"的内容了,经过本文的学习后,相信大家对切片传递的隐藏危机有哪些这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

函数 容量 数组 底层 指针 指向 类型 数据 结构 影响 输出 危机 例子 读者 长度 测试 内容 对象 情况 源码 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网加科技 科技企业和互联网企业 北京互联网科技 壹账通互联网科技 互联网巨头投资的硬核科技 互联网科技博主是什么意思 厦门比较好的科技互联网公司 和信科技工业互联网 电子科技大学互联网创业大赛 内蒙古九一互联网科技 互联网金融科技名词解释 工业互联网展湾科技股份有限公司 南通创西澳互联网科技有限公司 互联网营销推广黑科技 互联网科技股为什么大涨 广东鹅鹅鹅互联网科技有限公司 福州德业互联网科技 艺电能源互联网科技有限公司招聘 银川新华互联网科技学校原画 互联网的科技体现在哪里 互联网络科技 银川新华互联网科技职业技术培训学校图片 互联网科技股为什么大涨 济南小医互联网科技有限公司招聘 互联网硬科技公司 抖集互联网科技技术咨询 三亚科技创新互联网基地 亚光科技低轨卫星互联网 互联网领先科技成果宣传视频 上海互联网科技园景观
0