Skip to content

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了解细节。

/src/technology/dateblog/2025/04/20250430-go%E4%B8%AD%E7%9A%84%E6%8C%87%E9%92%88.html