
不像 Java 和 .NET,Go 语言为程序员提供了控制数据结构的指针的能力;但是,你不能进行指针运算。通过给予程序员基本内存布局,Go 语言允许你控制特定集合的数据结构、分配的数量以及内存访问模式,这些对构建运行良好的系统是非常重要的:指针对于性能的影响是不言而喻的,而如果你想要做的是系统编程、操作系统或者网络应用,指针更是不可或缺的一部分。
程序在内存中存储它的值,每个内存块(或字)有一个地址,通常用十六进制数表示,如:0x6b0820
或 0xf84001d7f0
。
Go 语言的取地址符是 &
,放到一个变量前使用就会返回相应变量的内存地址。
指针声明格式如下
1 |
var var_name *var-type |
符号 * 可以放在一个指针前,如 *zzd
,那么它将得到这个指针指向地址上所存储的值(通过变量地址取值);这被称为反引用(或者内容或者间接引用)操作符;另一种说法是指针转移。
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package main import "fmt" func main() { var i1 = 5 var zzd *int //声明一个int类型的指针变量 名为zzd zzd = &i1 //把i1变量的内存地址复制给zzd指针变量(&是寻址符) fmt.Printf("%d", *zzd) // 输出5 “*” 来获取指针变量所指向的内容 使用一个指针引用一个值被称为间接引用。 *zzd = 8 fmt.Printf("%d", *zzd) //输出8 这里的 * 号是一个类型更改器。 fmt.Println(i1) // 输出 8 } |
案例
1 2 3 4 5 6 7 8 9 10 |
package main import "fmt" func main() { var www = 1 fmt.Print(1 == *(&www)) } //输出 true |
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package main import "fmt" func main() { zzd := "zhengDa" var p *string = &zzd //可以直接初始化赋值 // p = &zzd *p = "wuguiyunwei" fmt.Println(p) //p(指针)输出 0xc0420301c0 fmt.Println(*p) //输出 wuguiyunwei fmt.Println(zzd) //输出 wuguiyunwei } |
注意
& 不能获取一个整数或常量的地址
案例
1 2 3 4 5 6 7 8 9 |
package main //import "fmt" func main() { const i = 5 p := &i //cannot take the address of w p2 := &10 //cannot take the address of 10 } |
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package main import "fmt" func Multiply(a, b int, reply *int) { *reply = a * b } func main() { n := 0 reply := &n Multiply(10, 5, reply) fmt.Print("Multiply:", *reply) } // 输出 50 |
附加知识
Go 语言和 C、C++ 以及 D 语言这些低级(系统)语言一样,都有指针的概念。但是对于经常导致 C 语言内存泄漏继而程序崩溃的指针运算(所谓的指针算法,如:pointer+2
,移动指针指向字符串的字节数或数组的某个位置)是不被允许的。Go 语言中的指针保证了内存安全,更像是 Java、C# 和 VB.NET 中的引用。
因此 c = *p++
在 Go 语言的代码中是不合法的。
指针的一个高级应用是你可以传递一个变量的引用(如函数的参数),这样不会传递变量的拷贝。指针传递是很廉价的,只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。
另一方面(虽然不太可能),由于一个指针导致的间接引用(一个进程执行了另一个地址),指针的过度频繁使用也会导致性能下降。
指针也可以指向另一个指针,并且可以进行任意深度的嵌套,导致你可以有多级的间接引用,但在大多数情况这会使你的代码结构不清晰。
如我们所见,在大多数情况下 Go 语言可以使程序员轻松创建指针,并且隐藏间接引用,如:自动反向引用。
对一个空指针的反向引用是不合法的,并且会使程序崩溃:
1 2 3 4 5 6 7 |
package main func main() { var p *int = nil *p = 0 } // in Windows: stops only with: <exit code="-1073741819" msg="process crashed"/> // runtime error: invalid memory address or nil pointer dereference |