golang的指针类型和c/c++的指针类型基本一样,但是多了几个限制:
1,int,int32等不同的指针类型不能相互转化.
2,指针类型不支持c/c++这样的指针运算。
1、uintptr
// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr
- uintptr 是一个整数类型(这个非常重要),注意,他不是个指针;
- 但足够保存任何一种指针类型。
uintptr
是一个地址数值,它不是指针,与地址上的对象没有引用关系,垃圾回收器不会由于有一个uintptr类型的值指向某对象而不回收该对象。
unsafe.Pointer
是一个指针,相似于C的void *
,它与地址上的对象存在引用关系,垃圾回收器会由于有一个unsafe.Pointer类型的值指向某对象而不回收该对象。
- 任何指针均可以转为
unsafe.Pointer
unsafe.Pointer
能够转为任何指针uintptr
能够转换为unsafe.Pointer
unsafe.Pointer
能够转换为uintptr
- 指针不能直接转换为
uintptr
理论上说指针不过是一个数值,即一个uint
,但实际上在go中unsafe.Pointer
是不能经过强制类型转换为一个uint
的,只能将unsafe.Pointer
强制类型转换为一个uintptr
。
2、unsafe 包支持了这些方法来完成【类型】=> uintptr 的转换:
func Sizeof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Alignof(x ArbitraryType) uintptr
3、使用示例
https://juejin.cn/post/7127600972573966373
3.1 常规类型互转
func Float64bits(f float64) uint64 {
return *(*uint64)(unsafe.Pointer(&f))
}
其实本质就是把 unsafe.Pointer
当成了一个媒介。用到了他可以从任意一个类型转换得来,也可以转为任意一个类型。
这样的用法有一定的前提:
- 转化的目标类型(uint64) 的 size 一定不能比原类型 (float64)还大(二者size都是8个字节);
- 前后两种类型有等价的 memory layout;
3.2 指针算数计算:Pointer => uintptr => Pointer
将一个指针转为 uintptr 将会得到它指向的内存地址,而我们又可以结合 SizeOf,AlignOf,Offsetof 来计算出来另一个 uintptr 进行计算。
这类场景最常见的是【获取结构体中的变量】或【数组中的元素】。
# 注意:变量到 uintptr 的转换以及计算必须在一个表达式中完成(需要保证原子性):
f := unsafe.Pointer(&s.f)
f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))
e := unsafe.Pointer(&x[i])
e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))
uintptr + offset 算地址,再跟 Pointer 转化其实是一个很强大的能力,我们再来看一个实际的例子:
package main
import (
"fmt"
"unsafe"
)
func main() {
length := 6
arr := make([]int, length)
for i := 0; i < length; i++ {
arr[i] = i
}
fmt.Println(arr)
// [0 1 2 3 4 5]
// 取slice的第5个元素:通过计算第1个元素 + 4 个元素的size 得出
end := unsafe.Pointer(uintptr(unsafe.Pointer(&arr[0])) + 4*unsafe.Sizeof(arr[0]))
fmt.Println(*(*int)(end)) // 4
fmt.Println(arr[4]) // 4
}
unsafe.Pointer 不能进行算数计算,uintptr 其实是很好的一个补充。
3.3 reflect 包中从 uintptr => Ptr
正例:
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))
反例:
u := reflect.ValueOf(new(int)).Pointer()
p := (*int)(unsafe.Pointer(u))