先总结结论:go语言中,所有的参数传递全都是值传递!!!,最终传递的都是一个副本,或者说拷贝了一份。
可以看一下人家官方文档怎么解释的:
As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an
intvalue to a function makes a copy of the
int, and passing a pointer value makes a copy of the pointer, but not the data it points to
翻译过来就是:
与 C 家族的所有语言一样,Go 中的所有内容都是按值传递的。也就是说,一个函数总是得到被传递的东西的副本,就好像有一个赋值语句将值赋给参数一样。例如,将int
值传递给函数会生成 的副本int
,传递指针值会生成指针的副本,但不会复制它指向的数据。
将实参的值传递给形参,形参是实参的一份拷贝,实参和形参的内存地址不同。函数内对形参值内容的修改,是否会影响实参的值内容,取决于参数是否是引用类型
将实参的地址传递给形参,函数内对形参值内容的修改,将会影响实参的值内容。Go语言是没有引用传递的,在C++中,函数参数的传递方式有引用传递。
// 这个容易理解,传递的是x的副本,函数内部的改变不影响外面的变量
func main() {
x := 1
fmt.Println(x) // 输出 1
modify(x)
fmt.Println(x) // 仍然输出 1,因为修改的是 x 的副本
}
func modify(x int) {
x = 2
}
package main
import "fmt"
func main() {
var s = []int64{1, 2, 3}
fmt.Printf("直接对原始切片取地址%v \n", &s)
fmt.Printf("原始切片的内存地址: %p \n", s)
fmt.Printf("原始切片第一个元素的内存地址: %p \n", &s[0])
modifySlice(s)
fmt.Printf("改动后的值是: %v\n", s)
}
func modifySlice(s []int64) {
fmt.Printf("直接对函数里接收到切片取地址%v\n", &s)
fmt.Printf("函数里接收到切片的内存地址是 %p \n", s)
fmt.Printf("函数里接收到切片第一个元素的内存地址: %p \n", &s[0])
s[0] = 10
}
直接对原始切片取地址&[1 2 3]
原始切片的内存地址: 0xc0000b8000
原始切片第一个元素的内存地址: 0xc0000b8000
直接对函数里接收到切片取地址&[1 2 3]
函数里接收到切片的内存地址是 0xc0000b8000
函数里接收到切片第一个元素的内存地址: 0xc0000b8000
改动后的值是: [10 2 3]
单从slice的结构体去看,slice的底层是一个数组指针,指向的是底层数组第一个元素,所以在进行传参的时候打印出来的地址一样,但是实际还是值传递,因为从modifySlice中是永远改变不了len和cap的,除非传递&slice
map的例子:
package main
import "fmt"
func main() {
m := make(map[string]int)
m["age"] = 1
fmt.Printf("原始map的内存地址是:%p\n", &m)
modifyMap(m)
fmt.Printf("改动后的值是: %v\n", m)
}
func modifyMap(m map[string]int) {
fmt.Printf("函数里接收到map的内存地址是:%p\n", &m)
m["age"] = 2
}
原始map的内存地址是:0xc00000e038
函数里接收到map的内存地址是:0xc00000e048
改动后的值是: map[age:2
这个就好理解了,modify之后和之前的内存地址不一样,说明是值传递。
package main
import "fmt"
type Person struct {
Name string
}
func main() {
p := &Person{
Name: "aa",
}
modifyStruct(p)
fmt.Println(p.Name) // aa
}
func modifyStruct(p *Person) {
p = nil
}
为什么这个打印出来是aa呢,不是传递指针了吗,为什么不是打印不是nil呢?
因为go函数传递都是值传递,即使传递了指针,实际就是对指针的一份拷贝,p = nil就是对新的指针指向的nil,并不是对原结构体指向nil,所以并不影响原结构体。
那我传递指针有什么作用呢?----->那就看下一个例子
package main
import "fmt"
type Person struct {
Name string
}
func main() {
p := &Person{
Name: "aa",
}
modifyStruct(p)
fmt.Println(p.Name) // bb
}
func modifyStruct(p *Person) {
p.Name = "bb"
}
这个为什么就是bb了呢?
因为拷贝过来的指针指向了原结构体的name字段,虽然拷贝了指针,但是这个指针指向了原来结构体的name字段,也就是aa,我将aa改变成了bb,所以最终影响了原结构体,实际上还是值传递,只是指向了原结构体字段,有了具体指向。