Go语言参数传递是传值还是传引用?首先我们先了解下两个概念:
- 传值(Pass By Value) 函数传递的总是原来这个东西的一个副本,一副拷贝。修改拷贝不会对原对象产生影响。
- 传引用(Pass By Reference) 函数传递的总是原来这个东西的指针引用,修改拷贝改变原对象的值。
通过上面的描述,很容易让人误认为Go语言参数传递是有传值和传引用的,恰恰相反,Go语言的参数传递是没有引用的,只有传值。如果现在你脑海里有跟我一样的迷惑请往下看个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package main
import "fmt"
func main() { i := 10 fmt.Printf("原始值的内存地址是:%p\n", &i) modifyInt(i) fmt.Println("i的新值为:", i) }
func modifyInt(i int) { fmt.Printf("函数里接收到的地址是:%p\n", &i) i = 2 }
|
运行这段代码,输出结果:
1 2 3
| 原始值的内存地址是:0xc000014088 函数里接收到的地址是:0xc000014090 i的新值为: 10
|
这里的地址每次运行结果都会不一样,但是我们可以看出来,此时函数内的i
是一份拷贝,有了一个新地址。修改他并不会影响原值。继续修改下代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package main
import "fmt"
func main() { i := 10 fmt.Printf("原始值的内存地址是:%p\n", &i) modifyInt(&i) fmt.Println("i的新值为:", i) }
func modifyInt(i *int) { fmt.Printf("函数里接收到的地址是:%p\n", &i) *i = 2 }
|
运行代码,输出结果:
1 2 3
| 原始值的内存地址是:0xc000082008 函数里接收到的地址是:0xc000090010 i的新值为: 2
|
值改变了!不是只传值吗, 怎么现在又可以传引用?如果不幸入坑,继续往下看,首先我们来看函数参数i
是什么类型,指针!那么&i
,没错就是指针的指针,我们打印一下原始指针
的地址.。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main
import "fmt"
func main() { i := 10 fmt.Printf("原始值的内存地址是:%p\n", &i) ip := &i fmt.Printf("原始指针的内存地址是:%p\n", &ip) modifyInt(ip) fmt.Println("i的新值为:", i) }
func modifyInt(i *int) { fmt.Printf("函数里接收到的指针地址是:%p\n", &i) *i = 2 }
|
输出结果:
1 2 3 4
| 原始值的内存地址是:0xc000080008 原始指针的内存地址是:0xc000090010 函数里接收到的指针地址是:0xc00000e018 i的新值为: 2
|
可以看出函数中拷贝了一份新的指针,用图表示如下:
验证代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( "fmt" "unsafe" )
func main() { i := 10 fmt.Printf("原始值的内存地址是:%p\n", &i) ip := &i fmt.Printf("原始指针的内存地址是:%p\n", &ip) fmt.Printf("原始指针实际值是:%v\n", unsafe.Pointer(ip)) modifyInt(ip) fmt.Println("i的新值为:", i) }
func modifyInt(i *int) { fmt.Printf("函数里接收到的指针地址是:%p\n", &i) fmt.Printf("函数里实际指针值是:%v\n", unsafe.Pointer(i)) *i = 2 }
|
输出结果:
1 2 3 4 5 6
| 原始值的内存地址是:0xc000088000 原始指针的内存地址是:0xc000082018 原始指针实际值是:0xc000088000 函数里接收到的指针地址是:0xc000094000 函数里实际指针值是:0xc000088000 i的新值为: 2
|
可以看出函数里和函数外的指针的值存储的是原始值的指针地址。
结论:Go语言参数传递是传值。需要注意的是Go中的map
、channel
、slice
都隐含一项指针,传值也是把指针传进去了,所以也是会修改到原值。