在本教程中,我们将学习指针在 Go 中是如何工作的,我们还将了解 Go 指针与其他语言(如 C 和 C++)中的指针有何不同。
什么是指针?
指针是存储另一个变量的内存地址的变量。
在上图中,变量b
的值为156
并存储在内存地址0x1040a124
中。变量a
保存b
的地址。现在说a
指向b
。
声明指针
*T 是指向 T 类型值的指针变量的类型。
让我们编写一个声明指针的程序。
1 | package main |
& 运算符用于获取变量的地址。在上述程序中的第 9 行,我们将b
的地址分配给变量a
。现在说a
指向b
。当我们打印a
中的值时,将打印b
的地址。该程序输出:
1 | Type of a is *int |
您可能会得到不同的b
的地址,因为 b
的位置可以在内存中的任何位置。
指针的零值
指针的零值为nil
。
1 | package main |
b
在上述程序中最初为 nil
,然后分配了 a
的地址。该程序输出:
1 | b is <nil> |
使用新函数创建指针
Go 还提供了一个方便的函数new
来创建指针。该new
函数将一个类型作为参数并返回一个指针,该指针指向作为参数传递的类型的新分配的零值。
下面的例子会让事情变得更清楚。
1 | package main |
在上面的程序中的第 8 行,我们使用new
函数来创建int
类型的指针。该函数将返回一个指向新分配的int
类型的指向该类型零值的指针。int
类型的零值为0
. 因此 size 将是 *int
类型并将指向0
,换句话说 *size
将会是 0
。
上面的程序将打印:
1 | Size value is 0, type is *int, address is 0x414020 |
取消引用指针
取消引用指针意味着访问指针指向的变量的值。*a
是服从 a 的语法。
让我们看看这在程序中是如何工作的。
1 | package main |
在上述程序的第 10 行,我们取消引用a
并打印它的值。正如预期的那样,它会打印 b 的值。程序的输出是:
1 | address of b is 0x1040a124 |
让我们再编写一个程序,在其中使用指针更改b
中的值。
1 | package main |
在上述程序的第 12 行中,我们将a
指向的值递增,这会改变 b 的值,因为 a 指向 b。因此 b 的值变为 256。程序的输出是:
1 | address of b is 0x1040a124 |
将指针传递给函数
1 | package main |
在上面的程序中的第 14 行,我们将保存 a 地址的指针变量 b 传递给change
函数。在change
函数内部,通过第 8 行中的取消引用来更改 a 的值。该程序输出:
1 | value of a before function call is 58 |
从函数返回指针
函数返回局部变量的指针是完全合法的。Go 编译器足够智能,它会在堆上分配这个变量。
1 | package main |
上面程序的第 9 行,我们从hello
函数中返回局部变量i
的地址。此代码的行为在 C 和 C++ 等编程语言中是未定义的,因为一旦hello
函数返回局部变量的指针,变量i
就会超出它的作用域。但是在 Go 中,编译器会进行转义分析,当i
脱离本地作用域时在堆上为其分配地址。因此,该程序将生效用并打印:
1 | Value of d 5 |
不要将指向数组的指针作为参数传递给函数,改用切片。
假设我们要对函数内部的数组进行一些修改,并且对函数内部的数组所做的更改应该对调用者可见。一种方法是将指向数组的指针作为参数传递给函数。
1 | package main |
上述程序的第 13 行,我们将数组a
的地址传递给modify
函数。在modify
函数的第 8 行内,我们取消引用 arr 并分配90
给数组的第一个元素。该程序输出[90 90 91]
\a[x] 是 (*a)[x] 的简写。所以上述程序中的 (*arr)[0] 可以替换为 arr[0]。让我们用这个速记语法重写上面的程序。
1 | package main |
该程序还输出[90 90 91]
尽管这种将指向数组的指针作为参数传递给函数并对其进行修改的方式是可行的,但这并不是在 Go 中实现这一点的惯用方式。我们有切片来专门实现这方面的操作。
让我们使用slices
重写相同的程序。
1 | package main |
在上面程序的第 13 行,我们将切片传递给modify
函数。切片的第一个元素在modify
函数内部更改为90
。该程序还输出[90 90 91]
。 所以不要传递指向数组的指针,而是使用切片:)。这段代码更简洁,并且在 Go 中是惯用的 :)。
Go不支持指针运算
Go 不支持其他语言(如 C 和 C++)中存在的指针算法。
1 | package main |
上述程序会抛出编译错误main.go:6: invalid operation: p++ (non-numeric type *[3]int)
这就是 Go 中的指针。祝你有美好的一天。