-
Notifications
You must be signed in to change notification settings - Fork 0
the parameters of the call passed by value or point
KerwinKoo edited this page Jan 2, 2016
·
9 revisions
首先,golang官方表示golang的函数调用参数均为值传递,不是指针传递或引用传递。经测试引申出来,当参数变量为指针或隐式指针类型,参数传递方式也是传值(指针本身的copy)。
“
after they are evaluated, the parameters of the call are passed by value to the
function and the called function begins execution.
”
关于最有争议的slice传参是传值还是传引用(或指针)的问题,做一个剖析。
首先,认为slice传参是引用传递的大都是根据以下代码情况得出的结果:
package main
import "fmt"
func main(){
slice := make([]int, 3, 5)
fmt.Println("before:", slice)
changeSliceMember(slice)
fmt.Println("after:", slice)
}
func changeSliceMember(slice []int) {
if len(slice) > 1 {
slice[0] = 9
}
}函数执行结果为:
befor:[0 0 0]
after:[9 0 0]
很显然,slice[0]值在函数内操作过之后确实在main中将结果保留了下来。但这不足以表明参数传递方式,参数slice无论值还是引用,不影响slice[0]地址的值。换句话说,只要参数是地址数据,都可以改变其指向的值的内容。
slice是结构体和指针的混合体,它的每项值,是以指针为内容存储存于values中,因此可以改变其值;slice属性values、count 和 capacity等均是值传递。但values的值(指针)所指向的堆栈,是存储单元,因此可以保留更改。
因此,以指针和结构体的角度看,都是值传递。
如下代码:
package main
import "fmt"
func main() {
slice := make([]int, 3, 5)
fmt.Println("main-befor send:", slice)
sliceValueAppend(slice)
fmt.Println("main-after send:", slice)
}
func sliceValueAppend(slice []int) {
slice = append(slice, 4)
fmt.Println("fun-after append:", slice)
}执行结果为:
main-befor send:[0 0 0]
fun-after append:[0 0 0 4]
main-after send:[0 0 0]
因此看出传输方式为值传递。
使用指针变量作为参数更清晰:
package main
func main() {
value := new(int)
modifyFunc(value)
println("main:", value)
}
func modifyFunc(value *int) {
value = nil
println("modifyFunc:", value)
}执行结果:
modifyFunc: 0x0
main: 0xc820049f30
可以看出,即使传值为指针,仍未改变变量value在main中的值。
golang在函数传参过程中均为值传递,但不代表golang没有传引用的地方。
golang闭包访问其外部环境变量时,是通过引用访问的。
package main
import "fmt"
func main() {
value := new(int)
fmt.Println("before:", value)
func () {
value = nil
}()
fmt.Println("after:", value)
}输出为:
before: 0xc82000a360
after: <nil>
其实闭包的引用传递,相当于全局变量的使用,只是对该值的处理过程进行闭包处理,没什么不好理解的。
- 在golang中,基础类型和自定义类型均以
传值的方式进行参数传递,包括但不限于slice、map、chan。 - golang对闭包传值方式是引用传值。