golang的指针类型和c/c++的指针类型基本一样,但是多了几个限制:
1,int,int32等不同的指针类型不能相互转化.
2,指针类型不支持c/c++这样的指针运算。
// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type 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
。
func Sizeof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Alignof(x ArbitraryType) uintptr
https://juejin.cn/post/7127600972573966373
func Float64bits(f float64) uint64 {return *(*uint64)(unsafe.Pointer(&f))
}
其实本质就是把 unsafe.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 := 6arr := 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)) // 4fmt.Println(arr[4]) // 4}
unsafe.Pointer 不能进行算数计算,uintptr 其实是很好的一个补充。
正例:
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))
反例:
u := reflect.ValueOf(new(int)).Pointer()
p := (*int)(unsafe.Pointer(u))