一、指针简介
当我们运行一段程序,首先程序会被从硬盘读取到内存中,每块内存都会有一个地址,用来标识数据在内存中存储的位置,而为了保存一个数据在内存中的地址,我们就需要指针变量。
区别于 C/C++ 中的指针,Go 语言中的指针不能进行偏移和运算,是安全指针,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:&
(取地址)和 *
(根据地址取值)。
二、指针的使用
1. 指针地址
Go 语言中使用 &
字符放在变量前面对变量进行取地址操作:
package main
import "fmt"
func main() {
a := "cdc"
b := &a
fmt.Printf("变量b的类型:%T\n", b) // 变量b的类型:*string
// 使用 %p 接收指针类型的值
fmt.Printf("变量b的值:%p\n", b) // 变量b的值:0xc000040240
// 变量b的值为变量a的内存地址,可以继续对变量b进行取地址操作
c := &b
fmt.Printf("b的内存地址为:%p\n", c) // b的内存地址为:0xc000006028
d := &c
fmt.Printf("c的内存地址为:%p\n", d) // c的内存地址为:0xc000006038
}
在内存中,上述几个变量的存储如下:
2. 指针类型
用于接收指针地址的变量对应的数据类型为指针类型,Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int
、*int64
、*string
等。指针类型也可以直接声明使用。
package main
import "fmt"
func main() {
// 声明一个整型类型的指针
var ptr1 *int
// 声明一个数组类型的指针
var ptr2 *[3]int
// 声明一个值为整型类型的数组
var ptr3 [3]*int
num := 10
ptr1 = &num
fmt.Println(ptr1) // 0xc00000a0b8
array1 := [3]int{10, 20, 30}
ptr2 = &array1
fmt.Println(ptr2) // &[10 20 30]
array2 := [3]*int{}
for i := 0; i < 3; i++ {
array2[i] = &i
}
ptr3 = array2
fmt.Println(ptr3) // [0xc00000a0f8 0xc00000a0f8 0xc00000a0f8]
}
3. 通过指针操作值
3.1 取值
在对普通变量使用 &
操作符取地址后会获得这个变量的指针,然后可以对指针使用 *
操作获取地址对应内存中存储的值。
package main
import "fmt"
func main() {
a := 10
b := &a
c := &b
d := &c
fmt.Printf("d:%v %v\n", d, *d) // d:0xc000006030 0xc000006028
fmt.Printf("c:%v %v\n", c, *c) // c:0xc000006028 0xc00000a0b8
fmt.Printf("b:%v %v\n", b, *b) // b:0xc00000a0b8 10
// 只要时指针,就可以取值
fmt.Println(***d) // 10
// 也可以通过取值操作对原来的值进行修改
*b = 100
fmt.Println(a) // 100
}
3.2 修改值
函数传值时,尤其是针对值类型的变量,都是先将变量的值拷贝一份,然后传递给函数使用,即在函数中修改变量的值不会影响原来的变量:
package main
import "fmt"
func changeValue1(num int) int {
num = 100
return num
}
func main() {
num := 10
changeValue1(num)
fmt.Println(num) // 10
}
如果想要原来的值也随着函数的操作而修改,可以通过指针传值的方式:
package main
import "fmt"
func changeValue2(num *int) {
*num = 100
}
func main() {
num := 10
changeValue2(&num)
fmt.Println(num) // 100
}
使用指针传参的好处,除了可以实现修改原来参数的值的功能,还可以减少传参时的内存消耗,例如:如果一个参数是一个字符串类型的值,且内容比较大,那所有要使用该变量的函数,都要将其拷贝一份,再拿过来使用,因此会占用很大的内存。但是如果只传递该变量的内存地址,则能减小内存不必要的浪费。