Appearance
指针
变量是一种使用方便的占位符,用于引用计算机内存地址。
而指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。
定义
- 使用
*类型
定义指针类型。 - 使用
&变量
获取变量地址。 - 使用
*指针变量
获取指针指向的值。
go
a := 10 /* 声明实际变量 */
b := &a /* 声明指针变量 */
fmt.Printf("a 变量的指针地址: %+v\n", &a)
/* 指针变量的值 */
fmt.Printf("b 指针变量的值: %+v\n", b)
/* 指针变量的存储地址 */
fmt.Printf("b 指针变量的指针地址: %x\n", &b)
/* 使用指针访问值 */
fmt.Printf("*b 变量的值: %+v\n", *b)
输出结果
a 变量的指针地址: 0x14000102020
b 指针变量的值: 0x14000102020
b 指针变量的指针地址: 0x140000a2028
*b 变量的值: 10
指针内存图
空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil
。
- nil 指针也称为空指针。
- nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
- 一个指针变量通常缩写为 ptr。
go
var ptr *int
fmt.Printf("ptr 的值为 : %x\n", ptr )
输出结果
ptr 的值为 : 0
空指针判断
go
var ptr *int
if ptr != nil {
fmt.Println("ptr 不是空指针")
} else {
fmt.Println("ptr 是空指针")
}
输出结果
ptr 是空指针
使用场景
指针在 Go 语言中有着广泛的应用场景,下面将从几个方面介绍指针的常见应用。
函数参数传递
通过指针,函数可以修改函数外部的变量。这在需要修改外部变量的值时非常有用,特别是在处理复杂数据结构或需要对全局状态进行修改的情况下。
go
package main
import "fmt"
func changeValue(ptr *int) {
*ptr = 20 // 修改指针指向的值
}
func main() {
x := 10
changeValue(&x) // 传递x的地址给changeValue函数
fmt.Println(x) // 输出修改后的x的值,即20
}
传递大对象
在函数参数传递时,如果直接传递大对象的副本,会产生额外的内存开销。通过传递指针,可以避免复制整个对象,提高程序的性能。
go
package main
type BigObject struct {
// 大对象的定义...
}
func processObject(obj *BigObject) {
// 对大对象进行处理...
}
func main() {
obj := BigObject{}
processObject(&obj) // 传递大对象的指针
}
不使用场景
- 不要对 map、slice、channel 这类引用类型使用指针
- 像 int、bool 这样的小数据类型没必要使用指针
- 如果需要并发安全,则尽可能地不要使用指针,使用指针一定要保证并发安全
- 指针最好不要嵌套,也就是不要使用一个指向指针的指针,虽然 Go 语言允许这么做,但是这会使你的代码变得异常复杂。