go中的指针
Go 保留了指针,在一定程度上保证了性能,同时为了更好的 GC 和安全考虑,又限制了指针的使用。
创建
关于指针有两个常用的操作符,一个是取地址符&,另一个是解引用符*。
取地址符&
对一个变量进行取地址,会返回对应类型的指针,例如:
go
func main() {
num := 2
p := &num
fmt.Println(p)
}指针存储的是变量num的地址
0xc00001c088解引用符*
解引用符则有两个用途,
访问指针所指向的元素
第一个用途是访问指针所指向的元素,也就是解引用,例如
go
func main() {
num := 2
p := &num
rawNum := *p
fmt.Println(rawNum)
}p是一个指针,对指针类型解引用就能访问到指针所指向的元素
2声明一个指针
第二个用途就是声明一个指针,例如:
go
func main() {
var numPtr *int
fmt.Println(numPtr)
}go
<nil>*int即代表该变量的类型是一个int类型的指针,不过指针不能光声明,还得初始化,需要为其分配内存,否则就是一个空指针, 无法正常使用。要么使用取地址符将其他变量的地址赋值给该指针,要么就使用内置函数new手动分配,例如:
go
func main() {
var numPtr *int
numPtr = new(int)
fmt.Println(numPtr)
}更多的是使用短变量
func main() {
numPtr := new(int)
fmt.Println(numPtr)
}new函数只有一个参数那就是类型,并返回一个对应类型的指针,函数会为该指针分配内存,并且指针指向对应类型的零值,例如:
go
func main() {
fmt.Println(*new(string))
fmt.Println(*new(int))
fmt.Println(*new([5]int))
fmt.Println(*new([]float64))
}go
0
[0 0 0 0 0]
[]禁止指针运算
在 Go 中是不支持指针运算的,也就是说指针无法偏移,先来看一段 C++代码
go
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = &arr[0];
cout << &arr << endl
<< p << endl
<< p + 1 << endl
<< &arr[1] << endl;
}go
0x31d99ff880
0x31d99ff880
0x31d99ff884
0x31d99ff884可以看出数组的地址与数字第一个元素的地址一致,并且对指针加一运算后,其指向的元素为数组第二个元素。 Go 中的数组也是如此,不过区别在于指针无法偏移,例如
go
func main() {
arr := [5]int{0, 1, 2, 3, 4}
p := &arr
println(&arr[0])
println(p)
// 试图进行指针运算
p++
fmt.Println(p)
}这样的程序将无法通过编译,报错如下
go
main.go:10:2: invalid operation: p++ (non-numeric type *[5]int)TIP
标准库unsafe提供了许多用于低级编程的操作,其中就包括指针运算,前往标准库-unsafe了解细节。