内容简介:首先说结论:在Go语言里,所有的参数传递都是值传递(传值),都是一个副本,一个拷贝,因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据。非引用类型(值类型):int,float,bool,string,以及数组和struct特点:变量直接存储值,内存通常在栈中分配,栈在函数调用完会被释放
首先说结论:在 Go 语言里,所有的参数传递都是值传递(传值),都是一个副本,一个拷贝,因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据。
非引用类型(值类型):int,float,bool,string,以及数组和struct
特点:变量直接存储值,内存通常在栈中分配,栈在函数调用完会被释放
引用类型:指针,slice,map,chan,接口,函数等
特点:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,通过GC回收
array
(1)使用值传递在函数间传递大数组
//声明一个需要8 MB的数组 var array [1e6]int // 将数组传递给函数 foo foo(array) // 函数 foo 接受一个 100 万个整型值的数组 ,实质是创建一个原数组的拷贝 func foo(array [1e6]int) { ... }
(2)使用指针在函数间传递大数组
//分配一个需要8 MB的数组 var array [1e6]int // 将数组的地址传递给函数 foo foo(&array) // 函数 foo 接受一个指向 100 万个整型值的数组的指针 ,实质是创建一个指向原数组的指针 func foo(array *[1e6]int) { ... }
slice
在函数间传递切片 ,也是以值的方式传递,传递的是slice的一个拷贝,但由于slice这个结构体自身尺寸较小,在 64 位架构的机器上,一个切片需要 24 字节的内存:指针字段需要 8 字节,长度和容量字段分别需要 8 字节,因此在函数间复制和传递切片成本很低。我们可以通过切片内部指向底层数据的指针来修改原数据。
// 分配包含 100 万个整型值的切片 slice := make([]int, 1e6) // 将 slice 传递到函数 foo slice = foo(slice) // 函数 foo 接收一个整型切片,并返回这个切片 func foo(slice []int) []int { ... return slice }
函数调用之后两个切片指向同一个底层数组
map
在函数间传递映射时也是值传递,只不过我们在创建map时,实际上是返回了一个指针类型:
// makemap implements a Go map creation make(map[k]v, hint) // If the compiler has determined that the map or the first bucket // can be created on the stack, h and/or bucket may be non-nil. // If h != nil, the map can be created directly in h. // If bucket != nil, bucket can be used as the first bucket. func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hma{ //省略无关代码 }
在map作为参数传递时,使用了这个指针的拷贝进行传递,属于值传递,因此我们可以通过map来修改原有数据,本质上是通过指针来操作。
package main import "fmt" func main() { persons := make(map[string]int) persons["张三"] = 19 mp := &persons fmt.Printf("原始map的内存地址是:%p\n", mp) modify(persons) fmt.Println("map值被修改了,新值为:", persons) } func modify(p map[string]int) { fmt.Printf("函数里接收到map的内存地址是:%p\n", &p) p["张三"] = 20 } //输出: //原始map的内存地址是:0xc00007c010 //函数里接收到map的内存地址是:0xc000084008 //map值被修改了,新值为: map[张三:20]
结构体
结构体本身属于值类型,在函数间作为参数传递时为该结构体的一个拷贝,属于值传递,因此也无法修改原数据,同样的,我们可以通过指针来实现修改原数据的目的
(1)使用值传递在函数间传递结构体
package main import "fmt" func main() { p:=Person{"张三"} fmt.Printf("原始Person的内存地址是:%p\n",&p) modify(p) fmt.Println(p) } type Person struct { Name string } func modify(p Person) { fmt.Printf("函数里接收到Person的内存地址是:%p\n",&p) p.Name = "李四" } //输出 //原始Person的内存地址是:0xc00008e040 //函数里接收到Person的内存地址是:0xc00008e050 //{张三}
(2)使用指针在函数间传递结构体
package main import "fmt" func main() { p := Person{"张三"} modify(&p) fmt.Println(p) } type Person struct { Name string } func modify(p *Person) { p.Name = "李四" } //输出;{李四}
channel
channel本质上和map一样,可以通过源码看到:
func makechan(t *chantype, size int64) *hchan { //省略无关代码 }
欢迎关注我们的微信公众号,每天学习Go知识
以上所述就是小编给大家介绍的《Golang参数传递问题》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- golang中的函数参数值传递和引用传递
- Python函数中参数是值传递,还是引用传递?
- react - 20 redux 中传递parameter, click中传递参数
- C# 之方法参数传递机制
- python中函数的参数传递
- Flink 中如何解析与传递参数
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learning PHP & MySQL中文版
车立红 / 中国电力出版社 / 2007-06 / 36.00元
《Learning PHP & MySQL中文版》将介绍程序、模板和数据库的工作原理,讲述如何应对其中的挑战,并彻底地探索这些技术。一起来看看 《Learning PHP & MySQL中文版》 这本书的介绍吧!