博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
go指针的一个小坑
阅读量:7196 次
发布时间:2019-06-29

本文共 3207 字,大约阅读时间需要 10 分钟。

几乎可以肯定的说,go语言中除了闭包在引用外部变量的时候是传引用的,其他的时候都是传值的。如果你说形参可以定义为指针。好吧,那么告诉你这个指针的值其实是按照传值的方式使用的。

下面看个很浅显的例子:

func stillTest(v int) {    v = v + 100}
i := 100fmt.Println("i ", i)stillTest(i)fmt.Println("after i ", i)

输出:

i  100after i  100

两个值是不会有什么区别的。但是指针就会有什么区别么?

func anotherStillTest(v *int) {    *v = *v + 100}
fmt.Println("i ", i)anotherStillTest(&i)fmt.Println("after i ", i)

输出:

i  100after i  200

你看到i的值改了,你大喊这难道不是传的引用吗。man,仔细看看下面的例子。

func addressStillTest(v *int) {    x := 456    v = &x}
x := 1000fmt.Println("x ", x)addressStillTest(&x)fmt.Println("after x ", x)

输出:

x  1000after 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 6Another 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 ...

 

坑已填平!

 

补充

坑其实只是勉强的算是填平了。比如,我现在需要在一个方法中修改一个结构体实例的值。

type Person struct {        Name string        Phone string}func main() {        session, err := mgo.Dial("server1.example.com,server2.example.com")        if err != nil {                panic(err)        }        defer session.Close()        // Optional. Switch the session to a monotonic behavior.        session.SetMode(mgo.Monotonic, true)        c := session.DB("test").C("people")        err = c.Insert(&Person{
"Ale", "+55 53 8116 9639"}, &Person{
"Cla", "+55 53 8402 8510"}) if err != nil { log.Fatal(err) } result := Person{} err = c.Find(bson.M{
"name": "Ale"}).One(&result) if err != nil { log.Fatal(err) } fmt.Println("Phone:", result.Phone)}

比如上例中,我需要从mongodb中取出结构体实例result的具体值,把一个指针传进,然后用给这个实例的每个成员分别赋值的方式可以得到数据库搜出来的具体的值。但是,如果我们一定要用指针替换的方式来取得这样的值该怎么办呢?

还是沿用最开始的例子里的type Dog struct结构体来定义测试方法:

func anotherAddressTest(d **Dog) {    a := &Dog{
"address dog", "address dog type"} *d = a }
// get address out of a func    var aad = &Dog{
"8", "9"} fmt.Println("Dog ", aad.Name, aad.Type) anotherAddressTest(&aad) fmt.Println("Address Dog ", aad.Name, aad.Type)

输出:

Dog  8 9Address Dog  address dog address dog type

可以看到,值被修改了。整个的东西其实在原理上来说都是一样的,作为函数的参数直接拷贝过来的指针如果被修改了是不会传回去任何的东西的。但是,如果指针所指向的内容被修改了,可以带到函数的外部。所以,这里使用了指向指针的指针,也就是二级指针。根据上面得出的院里二级指针作为参数如果被修改了不会带出道函数的外部,但是整个二级指针指向的内容如果修改了却可以带导函数的外部。

这些都是很浅显的东西,平时日日重复的代码生活过得居然都疏忽了。与诸君共勉吧,stay hungry,stay foolish!

 

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!
本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/4809853.html
,如需转载请自行联系原作者
你可能感兴趣的文章
在php中使用对称加密DES3,开发银行卡绑定,实名验证……
查看>>
Linux history
查看>>
活动安排
查看>>
Python中怎么进行单元测试
查看>>
Laravel之哈希/常用函数/分页
查看>>
spring 事务传播说明
查看>>
Codeforces 442A
查看>>
Please enable network time synchronisation in system settings
查看>>
Android Actionbar Tab 导航模式
查看>>
python+matplotlib+web.py
查看>>
springboot 使用maven 打包 报 (请使用 -source 7 或更高版本以启用 diamond 运算符) 错误解决办法...
查看>>
洛谷 P2290 [HNOI2004]树的计数(bzoj[1211])
查看>>
Linux系统管理
查看>>
virtualbox 相关操作
查看>>
git 和 github 的基本使用
查看>>
流水灯
查看>>
Dubbo系列(2)_RPC介绍
查看>>
mysql取字段名注意事项!!!!千万不能和关键字同名
查看>>
crontab
查看>>
c#程序中的AssemblyInfo.cs
查看>>