Go语言指针的用法和使用限制
kevinyan815 opened this issue · 0 comments
基础知识
指针保存着一个值的内存地址,类型 *T
代表指向T
类型值的指针。其零值为nil
。
&操作符为它的操作数生成一个指针。
i := 42
p = &i
*
操作符则会取出指针指向地址的值,这个操作也叫做“解引用”。
fmt.Println(*p) // 通过指针p读取存储的值
*p = 21 // 通过指针p设置p执行的内存地址存储的值
为什么需要指针类型呢?参考这样一个例子:
package main
import "fmt"
func double(x int) {
x += x
}
func main() {
var a = 3
double(a)
fmt.Println(a) // 3
}
在 double 函数里将 a 翻倍,但是例子中的函数却做不到。为什么?因为 Go 语言的函数传参都是 值传递
。double 函数里的 x 只是实参 a 的一个拷贝,在函数内部对 x 的操作不能反馈到实参 a。
如果这时,有一个指针就可以解决问题了。
package main
import "fmt"
func double(x *int) {
*x += *x
x = nil
}
func main() {
var a = 3
double(&a)
fmt.Println(a) // 6
p := &a
double(p)
fmt.Println(a, p == nil) // 12 false
}
很常规的操作,不用多解释。唯一可能有些疑惑的在这一句:
x = nil
这得稍微思考一下,才能得出这一行代码根本不影响的结论。因为参数都是值传递,所以 x 也只是对 &a 的一个拷贝。
*x += *x
这一句把 x 指向的值(也就是 &a 指向的值,即变量 a)变为原来的 2 倍。但是对 x 本身(一个指针)的操作却不会影响外层的 a,所以在double函数内部的 x=nil
不会影响外面。
Go语言指针的限制
出于安全方面的考虑,相较于 C 语言指针的灵活,Go语言里指针多了不少限制,不过这也算是 Go 的成功之处:既可以享受指针带来的便利,又避免了指针的危险性。
限制一:指针不能参与运算
来看一个简单的例子:
package main
import "fmt"
func main() {
a := 5
p := a
fmt.Println(p)
p = &a + 3
}
上面的代码将不能通过编译,会报编译错误:
invalid operation: &a + 3 (mismatched types *int and int)
也就是说 Go 不允许对指针进行数学运算。
限制二:不同类型的指针不允许相互转换。
下面的程序同样也不能编译成功:
package main
func main() {
var a int = 100
var f *float64
f = *float64(&a)
}
限制三:不同类型的指针不能比较,也不能相互赋值
这条限制同上面的限制二,因为指针之间不能做类型转换,所以也没法使用==
或者!=
进行比较了,同学不同类型的指针变量相互之间不能赋值。比如下面这样,也是会报编译错误。
package main
func main() {
var a int = 100
var f *float64
f = &a
}
Go语言的指针是类型安全的,但它有很多限制,所以 Go 还有非类型安全的指针,这就是 unsafe 包提供的 unsafe.Pointer。在某些情况下,它会使代码更高效,当然,也更危险。 具体用法放到后面 unsafe.Pointer 的模块里细说。