指针简介
变量
本质:对一块内存空间的命名
可以通过引用变量名使用这块内存空间存储的值
指针
指向这些变量值所在的内存地址的值
变量值所在内存地址的值 不等于 该内存地址存储的变量值
简单示例
a := 100
var ptr *int // 声明指针类型
ptr = &a // 初始化指针类型值为变量 a &为取地址符
fmt.Println(ptr)
fmt.Println(*ptr)
通过*ptr
获取指针指向内存地址存储的变量,称为间接引用
为什么要引入指针?
- 为程序员提供操作变量对应内存数据结构的能力
- 提高程序性能(指针可以直接指向内存地址,可以节省内存空间)
使用场景
- 类型指针:传递数据时无须拷贝数据,go中指针不能进行偏移和运算
- 切片:指向数组起始元素的指针,数量,容量
基本使用
指针的声明与初始化
之所以可以节省内存,是因为指针指向的内存地址大小固定,与存储值的类型无关
-
var ptr *int
ptr = &a
-
ptr := &a
自动根据a的类型判断指针类型 -
new()
函数:ptr := new(int)
格式化输出时,%p
标识指针类型
通过指针传值
func swap(a, b int) {
a, b = b, a
fmt.Println(a, b)
}
func main() {
a := 1
b := 2
swap(a, b)
fmt.Println(a, b)
}
2 1
1 2
func swap(a, b *int) {
*a, *b = *b, *a
fmt.Println(*a, *b)
}
func main() {
a := 1
b := 2
swap(&a, &b)
fmt.Println(a, b)
}
2 1
2 1
unsafe.Pointer
-
任何类型的指针
都可以被转化为unsafe.Pointer
; -
unsafe.Pointer
可以被转化为任何类型的指针
; -
uintptr
可以被转化为unsafe.Pointer
; -
unsafe.Pointer
可以被转化为uintptr
。
指针类型转换
unsafe.Pointer
万能指针,可表示任意可寻址的指针类型
i := 10
var p *int = &i
var fp *float32 = (*float32)(unsafe.Pointer(p))
*fp = *fp * 10
fmt.Println(i) // 100
注意:unsafe.Pointer
可以在任何指针类型之间转化,但这绕过了Go的类型安全机制
,所以不是一个安全的操作
指针运算实现
uintptr
是可用于存储指针的整型,而整型是可以进行代数运算
因此,将unsafe.Pointer
转化为uintptr
后,对应指针就具备了运算能力
arr := [3]int{1, 2, 3}
ap := &arr
sp := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(ap)) + unsafe.Sizeof(arr[0])))
*sp += 3
fmt.Println(arr)
[1 5 3]
这样就可以绕过Go指针的安全限制,实现指针的动态偏移
和代数运算
但这会导致即使数组越界,也不会报错,而是返回下一个内存地址存储的值
这就破坏了内存安全限制,所以要尽量避免使用,必须使用时也要非常谨慎