指针简介

变量其实就是对一块内存空间的命名,我们可以通过引用变量名来使用这块内存空间存储的值,而指针则是用来指向这些变量值所在内存地址的值。如果一个变量是指针类型的,那么就可以用这个变量来存储指针类型的值。

指针变量在传值时之所以可以节省内存空间,是因为指针指向的内存地址的大小是固定的,在 32 位机器上占 4 个字节,在 64 位机器上占 8 个字节,这与指针指向内存地址存储的值类型无关。

使用场景

指针在 Go 语言中有两个典型的使用场景:

  • 类型指针
  • 切片

作为类型指针时,允许对这个指针类型数据指向的内存地址存储值进行修改,传递数据时如果使用指针则无须拷贝数据从而节省内存空间,此外和 C 语言中的指针不同,Go 语言中的类型指针不能进行偏移和运算,因此更为安全。

切片由指向数组起始元素的指针、元素数量和容量组成,所以切片与数组不同,是引用类型,而非值类型。

示例

a := 1
b := &a // 变量b 引用 a 的值地址
*b = 2 // 用*表示指针类型
fmt.Println(a)
fmt.Println(b)
//2
//0xc0000aa058

上面代码中的 b 是一个指针类型,表示指向存储 int 类型值的指针。b 本身是一个内存地址值,所以需要通过内存地址进行赋值(通过 &a 可以获取变量 a 所在的内存地址),赋值之后,可以通过 获取指针指向内存地址存储的变量值(我们通常将这种引用称作「间接引用」)

基本使用

指针类型的声明和初始化

var ptr *int
fmt.Println(ptr)

a := 100
ptr = &a
fmt.Println(ptr)
fmt.Println(*ptr)

当指针被声明后,没有指向任何变量内存地址时,它的零值是 nil,然后我们可以通过在给定变量前加上取地址符 & 获取该变量对应的内存地址,再将其赋值给声明的指针类型,这样,就完成对指针类型的初始化了,接下来我们可以通过在指针类型前加上间接引用符 * 获取指针指向内存空间存储的变量值。

通过指针传值

这样做的好处是节省内存空间,此外还可以在调用函数中实现对变量值的修改,因为直接修改的是指针指向内存地址上存储的变量值,而不是值拷贝。

package main

import "fmt"

/**
交换值,引用方式
*/
func swap(a, b *int) {
	*a, *b = *b, *a
}

/**
交换值,普通方式
*/
func swap2(a, b int) (int, int) {
	return b, a
}
func main() {
	a := 3
	b := 4
	swap(&a, &b)
	fmt.Printf("a:%d,b:%d", a, b)

	a, b = swap2(a, b)
	fmt.Printf("a:%d,b:%d", a, b)

}
// 结果
a:4,b:3
a:3,b:4