8. 指针
8. 指针
在 C/C++ 中提供了指针这种类型,可以直接操作内存数据,在某些应用场景下非常实用。Golang 中也提供了指针,可以利用指针来简化一些任务的执行。虽然 Golang 提供了指针,但是由于操作指针的风险较大,考虑到这一点,Golang 同时也限制了指针的使用。
指针是什么?
指针是一个特殊类型的变量,用来存储其他变量的内存地址。指针变量指向一个内存地址,通过这个地址可以间接访问对应的变量。
声明指针
在 Golang 中声明一个指针变量需要使用 * 符号,放在变量名前,表示这是一个指针类型的变量。
var pointer *int // 一个指向整数类型的指针变量
获取变量地址
获取变量的地址需要使用 & 符号,放在变量名前,表示取该变量的地址。
var num = 10
pointer := &num // pointer 指向变量 num 的内存地址
访问指针的值
通过 * 操作符,可以访问指针变量所指向的值。
num := 10
ptr := &num
fmt.Println(ptr) // 输出 0x1400012a0b0
fmt.Println(*ptr) // 输出 10
作为参数
指针变量作为参数进行传递时,本质上还是传递的是指针的副本,只不过该副本中的内容和原指针变量的内容相同,所以通过地址来操作数据时,原数据也会发生改变。
// 交换两个数的值
func swap(i *int, j *int) {
var tmp = *i
*i = *j
*j = tmp
}
func main() {
x := 5
y := 8
fmt.Printf("x : %d, y : %d", x, y) // 输出 x : 5, y : 8
swap(&x, &y)
fmt.Printf("x : %d, y : %d", x, y) // 输出 x : 8, y : 5
}
指针运算
在 C/C++ 中支持指针运算,可以通过一个指针,访问其指定偏移量的数据,自由度较高。虽然在 Golang 中也提供了指针,但是实际上是禁止了大部分指针运算的,原因是指针运算比较危险,很容易造成系统崩溃。
下面是一段 C语言 的指针运算案例:
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int* p = arr; // 指向数组的第一个元素
// 使用指针遍历数组
for (int i = 0; i < 5; ++i) {
printf("Element %d: %d\n", i, *p);
++p; // 移动指针到下一个元素
}
// 重置指针回到数组的第一个元素
p = arr;
// 使用指针算术运算访问数组的特定元素
p = p + 2;
printf("Third element (using pointer arithmetic): %d\n", *p);
return 0;
}
在 C 语言中可以直接进行指针的运算,从而访问指定的数据,但这也就意味着,只要能够拿到对应的数据地址,即可访问,这是极度危险的。
使用 unsafe 包进行指针运算
如果你确实需要进行类似于 C/C++ 的指针算术运算,可以使用 unsafe 包。不过,这种方法应谨慎使用,因为它会绕过 Go 语言的内存安全检查,可能导致不可预知的行为。
空指针
当一个指针被定义后没有分配到任何变量的时候,它的值为 nil,即空指针
var pointer *int
指针变量声明后,默认零值为 nil,即空指针。
空指针判断
if pointer == nil {
// 空指针时执行
}
指针的指针
指针是指向变量内存地址的值,这个值同样也是存储在内存中,存储到内存中的值也必然会占用内存,也会有地址。故有指针的指针。
num := 10
var pointer1 = &num
var pointer2 = &pointer1
- pointer1 即为 num 的指针
- pointer2 即为 pointer1 的指针,即指针的指针
依次类推,可以有无限指针的循环嵌套
指针数组
指针数组本质上是一个数组,只不过数据类型从基本类型变成了指针类型,存储的内容都是指针。