几乎可以肯定的说,go语言中除了闭包在引用外部变量的时候是传引用的,其他的时候都是传值的。如果你说形参可以定义为指针。好吧,那么告诉你这个指针的值其实是按照传值的方式使用的。
下面看个很浅显的例子:
func stillTest(v int) { v = v + 100 }
i := 100 fmt.Println("i ", i) stillTest(i) fmt.Println("after i ", i)
输出:
i 100 after i 100
两个值是不会有什么区别的。但是指针就会有什么区别么?
func anotherStillTest(v *int) { *v = *v + 100 }
fmt.Println("i ", i) anotherStillTest(&i) fmt.Println("after i ", i)
输出:
i 100 after i 200
你看到i的值改了,你大喊这难道不是传的引用吗。man,仔细看看下面的例子。
func addressStillTest(v *int) { x := 456 v = &x }
x := 1000 fmt.Println("x ", x) addressStillTest(&x) fmt.Println("after x ", x)
输出:
x 1000 after x 1000
是的,第一个方法中传了一个地址进去,但是我们明显不是对地址做的任何修改操作,而是做了一个dereference操作。然后修改了变量的值。而在上面的这个例子中才是对地址的操作。我们在函数addressStillTest中试图修改x指向的地址,由于x的地址是传值操作的,也就是拷贝过来的,所以修改是无效的。最后的输出结果也说明了这一点。
所以在函数操作方面,任何的参数都是按照传值操作的方式执行的。不管是穿的指针还是一般的一个值都是传值使用的。
下面再看看这个结构体的例子。首先需要有这个:
type Dog struct { Name string Type string }
func addressTest(d *Dog) { a := &Dog{"another cute dog", "another type"} d = a }
输出:
Dog 5 6 Another Dog 5 6
对结构体直接做更换地址的操作还是不起作用。再一次表面函数的指针也是传值操作的。
如果要修改一个结构体呢?
func anotherTest(d *Dog) { a := &Dog{"another cute dog", "another type"} d.Name = a.Name d.Type = a.Type }
输出:
Dog cute dog ... Another Dog another cute dog another type
最后说明一个问题。在c,c++里如果从函数内部返回一个局部变量的指针的话是不对的。但是在Go里是可以的。Go的编译器会检查函数的局部变量指针是否会作为返回值给外部使用,如果是的话则将这个变量放在heap上延长其生命周期。
func test() *Dog { return &Dog{"cute dog", "..."} }
d := test() fmt.Println("Dog ", d.Name, d.Type)
输出:
Dog cute dog ...
坑已填平!