go slice作为函数参数是值传递

又名 “go slice作为函数参数是 “引用传递” 勘误 ”

1、问题描述

在很多Go的文章、博客中都有提到这样的内容:slice 在作为函数入参时采用的是引用(地址)传递。并且这几乎成了 “众所周知” 的知识点。

平时并没有留意别人是怎么看这里的,但最近面试和工作中发现上述观点几乎得到了其他人的共同认可。

不过在我初学Go的时候有幸看到过另一种说法,最近重新查阅资料整理代码给大家分享一下。

以下内容如果读者有什么不同见解和分析,欢迎留言或者发送邮件讨论交流。

2、证明分析

以下采用反证法

先假设 “slice 在作为函数入参时采用的是引用(地址)传递” 成立,那么根据引用的特征可以知道:函数外的slice(后续简写为 slice外 ) 和 函数内的slice(后续简写为 slice内)行为应当完全一致。

slice底层结构:

type slice struct { 
    array unsafe.Pointer 
    len int 
    cap int 
}

那么与之对应的应当有一下特征:

  1. slice.arrayslice内 改动后 slice外 也会发生对应的改变
  2. slice.lenslice内 改动后 slice外 也会发生对应的改变
  3. slice.capslice内 改动后 slice外 也会发生对应的改变

上述3个特征是必要条件,只要有一个不满足,则结论不成立。

2.1、一段代码示例

package main

import "fmt"

func main() {
    si := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Printf("%v  len %d\n", si, len(si))
    // fmt.Printf("1 %v  len %d cap %d %p\n", si, len(si), cap(si), &si)
    test1(si)
    fmt.Printf("%v  len %d\n", si, len(si))
    // fmt.Printf("2 %v  len %d cap %d %p\n", si, len(si), cap(si), &si)
}

func test1(si []int) {
    // fmt.Printf("3 %v  len %d cap %d %p\n", si, len(si), cap(si), &si)
    si = append(si[:3], si[4:]...)
    // fmt.Printf("4 %v  len %d cap %d %p\n", si, len(si), cap(si), &si)
}

该代码的运行结果如下:

[1 2 3 4 5 6 7 8 9]  len 9
[1 2 3 5 6 7 8 9 9]  len 9

2.2、推论结果

从代码示例及运行结果可以看出:

  1. 函数内部对 slice结构中数组(slice.array)的操作,对于函数外部来说部分成功了(第4个元素已经成功删除)
  2. 函数内部对 slice 结构中长度(slice.len)的操作,对于函数外部来说失败了
    第二次预期的输出是输出长度8、元素8个,但实际第二次输出长度是9,输出元素是9个
2.2.1、分析及结论:

实践后的结果与预期不符,假设成立的必要条件无法满足,也就是说:slice做为参数是引用(或地址)传递 不成立

即:slice作为参数是值传递 成立。

2.2.2、进一步解答:
  1. 问题一:既然是值传递,那为什么数组内容变了?

    因为slice整个结构是值传递,但slice中的数组是个 指针,在进行数据复制的过程中新的slice的数组内容使用的还是 原指针的值,因此函数内slice和外部的slice使用的是 同一份数据

    所以函数内部对 slice 数组的操作也会反馈到函数外的 slice上。

    但是长度、容量这两个是整型数值,不是指针类型,因此复制的过程中只是复制的内容,在函数内部的操作也不会影响外部。

参考资料及附录


本文由 qingchuwudi 译制或原创,除非另有声明,在不与原著版权冲突的前提下,本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

知识共享许可协议

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值