Golang 中的 unsafe.Pointer 和 uintptr

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))

3.4 sync.Pool

你可能感兴趣的:(Golang,golang,c++,数据结构)