Go语言的传参和传引用
传参和传引用的问题很多非官方的文档和教材(包括一些已经出版的图书),对Go语言的传参和引用的讲解 都有很多问题. 导致众多Go语言新手对Go的函数参数传参有很多误解. 而传参和传引用是编程语言的根本问题,如果这个问题理解错误可能会导致很多问题. 传slice不是传引用!首先,Go语言的函数调用参数全部是传值的,包括 具体请看Go语言的规范:
什么叫传引用?比如有以下代码: var a Object doSomething(a) // 修改a的值 print(a) 如果函数 为什么传slice不是传引用?我们构造以下的代码: func main() { a := []int{1,2,3} fmt.Println(a) modifySlice(a) fmt.Println(a) } func modifySlice(data []int) { data = nil } 其中 [1 2 3] [1 2 3] 说明 为什么很多人误以为slice是传引用呢?可能是FAQ说slice是引用类型,但并不是传引用! 下面这个代码可能是错误的根源: func main() { a := []int{1,3} fmt.Println(a) modifySliceData(a) fmt.Println(a) } func modifySliceData(data []int) { data[0] = 0 } 输出为: [1 2 3] [0 2 3] 函数 但是请注意: 修改通过函数修改参数内容的机制有很多,其中传参数的地址就可以修改参数的值(其实是修改参数中指针指向的数据),并不是只有引用一种方式! 传指针和传引用是等价的吗?比如有以下代码: func main() { a := new(int) fmt.Println(a) modify(a) fmt.Println(a) } func modify(a *int) { a = nil } 输出为: 0xc010000000 0xc010000000 可以看出指针 因此,函数参数传传指针也是传值的,并不是传引用! 所有类型的函数参数都是传值的!包括 但是因为 重点归纳如下:
那Go语言有传引用的说法吗?Go语言其实也是有传引用的地方的,但是不是函数的参数,而是闭包对外部环境是通过引用访问的. 查看以下的代码: func main() { a := new(int) fmt.Println(a) func() { a = nil }() fmt.Println(a) } 输出为: 0xc010000000 <nil> 因为闭包是通过引用的方式使用外部环境的 比如下面2段代码的输出是截然不同的,原因就是第二个代码是通过闭包引用的方式输出 for i := 0; i < 5; i++ { defer fmt.Printf("%d ",i) // Output: 4 3 2 1 0 } fmt.Printf("n") for i := 0; i < 5; i++ { defer func(){ fmt.Printf("%d ",i) } () // Output: 5 5 5 5 5 } 像第二个代码就是于闭包引用导致的副作用,回避这个副作用的办法是通过参数传值或每次闭包构造不同的临时变量: // 方法1: 每次循环构造一个临时变量 i for i := 0; i < 5; i++ { i := i defer func(){ fmt.Printf("%d ",i) } () // Output: 4 3 2 1 0 } // 方法2: 通过函数参数传参 for i := 0; i < 5; i++ { defer func(i int){ fmt.Printf("%d ",i) } (i) // Output: 4 3 2 1 0 } 什么是引用类型,和指针有何区别/联系 ?在Go语言的官方FAQ中描述,
我个人理解,引用类型和指针在底层实现上是一样的. 但是引用类型在语法上隐藏了显示的指针操作. 引用类型和函数参数的传引用/传值并不是一个概念. 我们知道 要用好Go语言的引用类型,必须要了解一些底层的结构(特别是 我们可以自己给Go语言模拟一个引用类型. 我们可以将值类型特定的数组类型定义为一个引用类型(同时提供一个构造函数): type RefIntArray2 *[2]int func NewRefIntArray2() RefIntArray2 { return RefIntArray2(new([2]int)) } 这样我们就可以将 func main() { refArr2 := NewRefIntArray2() fmt.Println(refArr2) modifyRefArr2(refArr2) fmt.Println(refArr2) } func modifyRefArr2(arr RefIntArray2) { arr[0] = 1 } 输出为: &[0 0] &[1 0] 之所以选择数组作为例子,是因为Go语言的数组指针可以直接用 注: 本节根据 @hooluupog 和 @LoongWong 的评论做的补充. 总结
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |