对于Go语言,严格意义上来讲,只有一种传递,也就是按值传递(by value)。当一个变量当作参数传递的时候,会创建一个变量的副本,然后传递给函数或者方法,你可以看到这个副本的地址和变量的地址是不一样的。

当变量当做指针被传递的时候,一个新的指针被创建,它指向变量指向的同样的内存地址,所以你可以将这个指针看成原始变量指针的副本。当这样理解的时候,我们就可以理解成Go总是创建一个副本按值转递,只不过这个副本有时候是变量的副本,有时候是变量指针的副本。

Go语言保留着C中值和指针的区别,但是对于指针繁琐用法进行了大量的简化,引入引用的概念。所以在Go语言中,你几乎不用担心会因为直接操作内寸而引起各式各样的错误。Go语言的指针,基本上只剩下用于区分 byref 和 byval 语义。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// &: 取地址
// *: 解析地址

var p *int // p 的类型是: int型的指针

// Foo, Bar的类型是指针
type TestItem struct {
    Foo *string
    Bar *string
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package basic

import (
    "fmt"
)

func PointerTest() {

    var i int  // i 的类型是int型
    i = 1      // i 的值为 1;
    var p *int // p 的类型是[int型的指针]
    p = &i     // p 的值为 [i的地址]

    fmt.Printf("i=%d;p=%d;*p=%d\n", i, p, *p)

    *p = 2 // *p 的值为 [[i的地址]的指针] (其实就是i嘛),这行代码也就等价于 i = 2
    fmt.Printf("i=%d;p=%d;*p=%d\n", i, p, *p)

    i = 3 // 验证想法
    fmt.Printf("i=%d;p=%d;*p=%d\n", i, p, *p)

}

这段代码执行结果:

i=1;p=0x4212f100;_p=1

i=2;p=0x4212f100;_p=2

i=3;p=0x4212f100;*p=3

函数的参数传递可以看下面例子:

package main

import “fmt”

type abc struct{

v int

}

func (a abc)aaaa(){ //传入的是值,而不是引用

a.v=1

fmt.Printf(“1:%d\n”,a.v)

}

func (a *abc)bbbb(){ //传入的是引用,而不是值

fmt.Printf(“2:%d\n”,a.v)

a.v=2

fmt.Printf(“3:%d\n”,a.v)

}

func (a *abc)cccc(){ //传入的是引用,而不是值

fmt.Printf(“4:%d\n”,a.v)

}

func main(){

aobj:=abc{} //new(abc);

aobj.aaaa()

aobj.bbbb()

aobj.cccc()

}

输出结果:

1:1

2:0

3:2

4:2

传值与传指针

当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。

传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。

Go语言中string,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注: 若函数需改变slice的长度,则仍需要取地址传递指针)

要访问指针 p 指向的结构体中某个元素 x,不需要显式地使用 * 运算,可以直接 p.x ;

 

一个稍微复杂的例子

package main

import “fmt”

type S map[string][]string

func Summary(paramstring)(s*S){

s=&S{

“name”:[]string{param},

“profession”:[]string{“Javaprogrammer”,“ProjectManager”},

“interest(lang)":[]string{“Clojure”,“Python”,“Go”},

“focus(project)":[]string{“UE”,“AgileMethodology”,“SoftwareEngineering”},

“hobby(life)":[]string{“Basketball”,“Movies”,“Travel”},

}

return s

}

func main(){

s:=Summary(“Harry”)

fmt.Printf(“Summary(address):%v\r\n”,s)

fmt.Printf(“Summary(content):%v\r\n”,*s)

}

输出:

Summary(address): 0x42131100

Summary(content): map[profession:[Java programmer Project Manager] interest(lang):[Clojure Python Go] hobby(life):[Basketball Movies Travel] name:[Harry] focus(project):[UE Agile Methodology Software Engineering]]

exit code 0, process exited normally.

参考资料:

使用Go语言一段时间的感受

<a href="http://blog.jobbole.com/14386/">使用Go语言一段时间的感受</a>

T的副本创建

package main
import "fmt"
type Bird struct {
    Age  int
    Name string
}
func passV(b Bird) {
    b.Age++
    b.Name = "Great" + b.Name
    fmt.Printf("传入修改后的Bird:\t %+v, \t内存地址: %p\n", b, &b)
}
func main() {
    parrot := Bird{Age: 1, Name: "Blue"}
    fmt.Printf("原始的Bird:\t\t %+v, \t\t内存地址: %p\n", parrot, &parrot)
    passV(parrot)
    fmt.Printf("调用后原始的Bird:\t %+v, \t\t内存地址: %p\n", parrot, &parrot)
}

*T的副本创建

package main
import "fmt"
type Bird struct {
    Age  int
    Name string
}
func passP(b *Bird) {
    b.Age++
    b.Name = "Great" + b.Name
    fmt.Printf("传入修改后的Bird:\t %+v, \t内存地址: %p, 指针的内存地址: %p\n", *b, b, &b)
}
func main() {
    parrot := &Bird{Age: 1, Name: "Blue"}
    fmt.Printf("原始的Bird:\t\t %+v, \t\t内存地址: %p, 指针的内存地址: %p\n", *parrot, parrot, &parrot)
    passP(parrot)
    fmt.Printf("调用后原始的Bird:\t %+v, \t内存地址: %p, 指针的内存地址: %p\n", *parrot, parrot, &parrot)
}

http://blog.jobbole.com/14386/embed/#?secret=rAI8Jn2kEL http://my.oschina.net/nalan/blog/77373 http://ilovers.sinaapp.com/drupal/node/33 http://www.cnblogs.com/ghj1976/archive/2013/02/28/2936595.html https://colobu.com/2017/01/05/-T-or-T-it-s-a-question/