go指针的一个小坑

简介:

几乎可以肯定的说,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 ...

 

坑已填平!

 

补充

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

复制代码
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 9
Address Dog  address dog address dog type

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

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

 

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 330987132 | Go:217696290 | Python:336880185 | 做人要厚道,转载请注明出处!http://www.cnblogs.com/sunshine-anycall/p/4809853.html
相关文章
|
6月前
|
Java 编译器 Go
Go 语言 入门 && 基于 GoLand 2023.1 创建第一个Go程序
Go 语言 入门 && 基于 GoLand 2023.1 创建第一个Go程序
57 0
|
2月前
|
Go
在Go中什么时候使用指针?
在Go中什么时候使用指针?
15 1
|
8月前
|
存储 Go
Go的指针注意事项
指针使用规范很重要。
48 0
|
4月前
|
存储 Cloud Native 安全
GO 中的指针?
GO 中的指针?
go的接收器什么时候该用指针?|Go主题月
指针的两个关键符号 *:返回所引用变量内存地址上的值 &:返回变量的内存地址
182 0
|
Go
go导入包及函数声明【我的go学习第四课】
go导入包及函数声明【我的go学习第四课】
80 0
Go容易搞错的知识点汇总
这篇文章汇总了我在开发和刷面试题过程中遇到的容易搞错的知识点,关键部分也都为大家写了代码示例。
Go容易搞错的知识点汇总
|
JSON Go 数据格式
Go 开发常用操作技巧--结构体
结构体是由一系列相同或不相同类型数据组成的数据集合
54 0
Go 开发常用操作技巧--结构体
|
存储 Java 编译器
Go 语言快速入门指南:Go 指针
我们都知道,Java、C#、Python 都 因为指针的复杂而避开了指针的用法,改成了引用。 Go 语言作为 21 世纪的 C 语言,自然保留了 C 语言的许多特性,指针就是其一。但相比于 C 的指针,Go 对指针做了很多限制。 这一篇,就来学习 Go 指针的各种相关知识。
Go 语言快速入门指南:Go 指针
第二章 go指针
指针 先来看一段代码
91 0
第二章 go指针