Go语言中的指针

Go 语言为程序员提供了控制数据结构的指针的能力;但是,你不能进行指针运算。 Go 语言允许你控制特定集合的数据结构、分配的数量以及内存访问模式,这些对构建运行良好的系统是非常重要的:指针对于性能的影响是不言而喻的,而如果你想要做的是系统编程、操作系统或者网络应用,指针更是不可或缺的一部分。

Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。

var i1 = 5
fmt.Printf("An integer: %d, it's location in memory: %p\n", i1, &i1)

这个地址可以存储在一个叫做指针的特殊数据类型中,在本例中这是一个指向 int 的指针,即 i1:此处使用 *int 表示。如果我们想调用指针 intP,我们可以这样声明它:

var intP *int

然后使用 intP = &i1 是合法的,此时 intP 指向 i1。

(指针的格式化标识符为 %p

intP 存储了 i1 的内存地址;它指向了 i1 的位置,它引用了变量 i1。

一个指针变量可以指向任何一个值的内存地址

你可以在指针类型前面加上 * 号(前缀)来获取指针所指向的内容,这里的 * 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。

当一个指针被定义后没有分配到任何变量时,它的值为 nil

一个指针变量通常缩写为 ptr

在书写表达式类似 var p *type 时,切记在 * 号和指针名称间留有一个空格,因为 - var p*type 是语法正确的,但是在更复杂的表达式中,它容易被误认为是一个乘法表达式!

符号 * 可以放在一个指针前,如 *intP,那么它将得到这个指针指向地址上所存储的值;这被称为反引用(或者内容或者间接引用)操作符;另一种说法是指针转移。

对于任何一个变量 var, 如下表达式都是正确的:var == *(&var)

package main
import "fmt"
func main() {
	var i1 = 5
	fmt.Printf("An integer: %d, its location in memory: %p\n", i1, &i1)
	var intP *int
	intP = &i1
	fmt.Printf("The value at memory location %p is %d\n", intP, *intP)
}

输出:

An integer: 5, its location in memory: 0x24f0820
The value at memory location 0x24f0820 is 5
package main
import "fmt"
func main() {
	s := "good bye"
	var p *string = &s
	*p = "ciao"
	fmt.Printf("Here is the pointer p: %p\n", p) // prints address
	fmt.Printf("Here is the string *p: %s\n", *p) // prints string
	fmt.Printf("Here is the string s: %s\n", s) // prints same string
}

输出:

Here is the pointer p: 0x2540820
Here is the string *p: ciao
Here is the string s: ciao

你不能得到一个文字或常量的地址,例如:

const i = 5
ptr := &i //error: cannot take the address of i
ptr2 := &10 //error: cannot take the address of 10

Go 语言中的指针保证了内存安全,更像是 Java、C# 和 VB.NET 中的引用。 因此 c = *p++ 在 Go 语言的代码中是不合法的。

指针的一个高级应用是你可以传递一个变量的引用(如函数的参数),这样不会传递变量的拷贝。指针传递是很廉价的,只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,直到没有任何指针指向它们,所以从它们被创建开始就具有相互独立的生命周期。

另一方面(虽然不太可能),由于一个指针导致的间接引用(一个进程执行了另一个地址),指针的过度频繁使用也会导致性能下降。

指针也可以指向另一个指针,并且可以进行任意深度的嵌套,导致你可以有多级的间接引用,但在大多数情况这会使你的代码结构不清晰。

如我们所见,在大多数情况下 Go 语言可以使程序员轻松创建指针,并且隐藏间接引用,如:自动反向引用。

对一个空指针的反向引用是不合法的,并且会使程序崩溃:

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

发表评论

电子邮件地址不会被公开。 必填项已用*标注