关于GO语言中值类型与引用类型的思考
2018-07-20 19:32
295 查看
首先需要明确的是,在GO语言中,我们利用fmt.Printf("%p", ptr)来显示指针的地址。
先看结论,这里引用GO语言圣经中3.1.2章节中关于值语义和引用语义的论述:
可以得知切片、map、channel和接口是引用类型。
[code]package main import ( "fmt" ) func main() { a := make(map[int]string, 1) a[1] = "a" fmt.Printf("a: %p\n", a) fmt.Printf("&a: %p\n", &a) a[3] = "c" fmt.Printf("a: %p\n", a) fmt.Printf("&a: %p\n", &a) // a的cap为1, 添加a[3]发生扩容, 但是a和&a都没变, 说明扩容不会改变地址 modMap(a) fmt.Println("a value: ", a) // 修改后的a变成{1:a 3:c 5:t} fmt.Printf("a after mod: %p\n", a) fmt.Printf("&a after mod: %p\n", &a) // a在函数修改之后a和&a也没变 newA := a fmt.Printf("newA: %p\n", newA) // a赋值给了newA, 地址不变 fmt.Printf("&newA: %p\n", &newA) // a和newA两个变量名不同所以 &newA与&a不一样 newA = map[int]string{11:"aa"} // newA从新赋值 fmt.Printf("newA: %p\n", newA) // newA地址变化 fmt.Printf("&newA: %p\n", &newA) // 但&newA不变 modPtr(&newA) fmt.Println("newA after modPtr: ", newA) fmt.Printf("newA: %p\n", newA) // newA值变化 fmt.Printf("&newA: %p\n", &newA) // 但&newA不变 c := 10 modInt(c) fmt.Printf("&c after mod: %p\n", &c) } func modMap(p map[int]string) { p[1] = "aa" //p = nil fmt.Println("p value: ", p) fmt.Printf("a in func: %p\n", p) fmt.Printf("&a in func: %p\n", &p) // 这里&a变了, 但是a还是没变 } func modInt(n int) { n++ fmt.Printf("&c in func: %p\n", &n) // 这里的&n 和外边的 &c不一样, 所以我还是觉得&c和&a一样, 确实是存的变量名的地址 // 由于传值导致了变量拷贝, 所以地址是不同的 } func modPtr(a *map[int]string){ *a = nil fmt.Printf("newA in ptrfunc: %p\n", a) // 由于是指针,对应函数外&newA地址,不变 fmt.Printf("&newA in ptrfunc: %p\n", &a) // 对应函数内变量名所在地址 }
返回结果如下:
[code]a: 0xc420076180 &a: 0xc42000c028 a: 0xc420076180 &a: 0xc42000c028 p value: map[1:aa 3:c] a in func: 0xc420076180 &a in func: 0xc42000c038 a value: map[1:aa 3:c] a after mod: 0xc420076180 &a after mod: 0xc42000c028 newA: 0xc420076180 &newA: 0xc42000c040 newA: 0xc420076210 &newA: 0xc42000c040 newA in ptrfunc: 0xc42000c040 &newA in ptrfunc: 0xc42000c048 newA after modPtr: map[] newA: 0x0 &newA: 0xc42000c040 &c in func: 0xc4200160d0 &c after mod: 0xc4200160c8
其中相关的猜测写在了注释中,根据结果总结如下:
1、对于map一类本身是指针的类型,map变量名为地址,&map为变量名存在的地址;
2、当用一个变量创建一个引用语义的数据时(slice/map),这个变量其实“是”(相当于)指针,函数传参时进行了拷贝,拷贝的是指针,通过这种方式达到共享同一份数据的目的。 而如果对指针变量重新复制的话(=nil或赋新值),其实是将copy的这个指针指向了新值而已,外面的那个指针变量还是指向的原来的数据。
3、函数的调用为值传递,而并非用slice/map时就是引用传递,但需要注意的时,GO语言中传指针类型确实为引用(废话都指针了还不是引用?),如例子中modPtr方法所示。由于是指针,在函数内进行赋值nil的操作影响了函数外的map。
4、在实际过程如果传参使用了引用类型,注意修改值时是否会发生变化,避免引起错误。
以上为本人与论坛网友Leigg关于传参问题讨论得到的结论,谨以此博客做过程记录,原贴链接如下:https://studygolang.com/topics/5997,希望有大神予以指正,谢谢!
阅读更多相关文章推荐
- 关于值类型和引用类型的思考
- 关于强弱,静动语言类型的思考
- 关于JavaScript预编译和执行顺序以及函数引用类型的思考
- go语言笔记——切片底层本质是共享数组内存!!!绝对不要用指针指向 slice切片本身已经是一个引用类型就是指针
- 关于强弱,静动语言类型的思考
- 关于C#编程中引用与值类型赋值的一些容易犯错的地方
- 关于引用类型ref的运用
- Go语言引用css和js文件
- Go 语言的基本数据类型
- Julia: 引用与copy: 关于数组、自定义类型与初始化
- Go游戏服务器开发的一些思考(二十一):Go语言的两处脑残设定
- js class模型 关于 初始化key值为非基本类型是 实例之间是引用该变量
- JavaScript给input的value赋值引发的关于基本类型值和引用类型值问题
- 关于.NET中的值类型和引用类型
- 关于 Go 中 Map 类型和 Slice 类型的传递
- Go 语言中的方法,接口和嵌入类型
- 关于C#间接继承的一些思考:如何修改继承函数的返回值类型
- GO语言学习-复杂类型(2)
- 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
- Go 学习笔记:Go 语言数据类型