指针
激动人心的指针来了!请记住,指针在Go中的唯一目的就是传递引用。
首先,我们回忆一下当年C语言的指针三剑客:
int a = 3; // 定义int变量
int &r = a; // 定义引用(Go没有这个写法)
int *p = &a; // 取地址
接下来看这个case:
package main
import "fmt"
// 传递值
func zeroval(ival int) {
ival = 0
}
// 传递引用
func zeroptr(iptr *int) {
*iptr = 0
}
func main() {
i := 1
fmt.Println("initial:", i)
// 将值进行传递
zeroval(i)
fmt.Println("zeroval:", i)
// 取址符号,将地址传给引用变量
zeroptr(&i)
fmt.Println("zeroptr:", i)
// 取址符号,直接取出地址
fmt.Println("pointer:", &i)
}
上面的程序输出:
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0xc000128058
指针的本质
以前总认为,指针就是地址,但实际上更准确地说,指针是存放地址的类型,不只是地址本身,可以说:指针是地址类型。到此打住,不必再纠结指针到底是不是一个int32或int64,只要记住它是个类型就好了。类型在Go编译层面有意义,到了汇编语言,全都是寻址指令。
指针的写法
以前学C语言指针时,最困惑的一个问题:C语言的指针符号到底靠近变量类型还是变量名?先说结论:*靠近类型还是变量名都可以,编译器都会认为是指针变量,本质是一样的;
int* p; // 字面意思:p是int类型的指针
int *p; // 字面意思:声明指针p并指向int类型
但如果靠近int,虽然更贴近字面意思,但在定义多变量的时候会有歧义:
int* p,q;
int *p,q;
上面两个声明效果完全一样,但前者在字面上存在歧义,因此一般会将*靠近变量。
回到Go,细心的你已经发现了,Go不存在这样的歧义,因为在定义指针时,*是贴近类型的,而在使用指针时,*直接贴近变量,因为Go不存在上面描述的多变量歧义:
func printPointer(s *string) {
fmt.Println("s:", s)
}
func printValue(s *string) {
fmt.Println("*s:", *s)
}
func main() {
s := "go"
var num, another *string = &s, &s
printPointer(num)
printValue(another)
}
上面的程序输出:
s: 0xc000058230
*s: go
s: 0xc000058230
*s: go
每日一Tip
可以通过sizeof(p)来输出指针变量p的长度。