unsafe.pointer 与 uintptr的区别| 8月更文挑战

1,877 阅读2分钟

unsafe.pointer与unintptr这两个东西在源码中出现的比较多,自己在业务上使用的比较少。但是面试中面试官还是挺喜欢问的。

unsafe.pointer

从名字来看它是不安全的和指针相关,unsafer.pointer主要的功能就是不同类型指针间的转换。

不同类型之间不能直接转换

func main() {
   var a *int8
   var b *int16
   a = new(int8)
   b = new(int16)
   *b = 10
   *a = *b
   fmt.Println(a)
}
  1. 初始化一个指针类型的int8 a
  2. 初始化一个指针类型的int16 b
  3. 给指针b指向的内存赋值10
  4. 指针a内存赋值指针b的内存值

结果:cannot use *b (type int16) as type int8 in assignment
go是强类型语言,这种即使都是int,这样转换也是不行的,于是出现了unsafe.pointer。

通过unsafe.pointer来花式转换

var a *int8
var b *int16
a = new(int8)
b = new(int16)
*b = 10
upb := unsafe.Pointer(b)
b_int8ptr := (*int8)(upb)
*a = *(b_int8ptr)
fmt.Println(*a)
  1. 初始化一个指针类型的int8 a
  2. 初始化一个指针类型的int16 b
  3. 给指针b指向的内存赋值10
  4. 通过unsafe.pointer获取b的指针upb
  5. 把upb转成*int8指针 b_int8ptr
  6. 获取b_int8ptr的内存值赋值给a的地址指向的空间 结果:10
    unsafer.pointer可以转换不同类型的指针。

unintptr

uintptr 实际上就是一个 uint 用来表示地址的。

var a, b uintptr
a = 10
b = 10
fmt.Println(a + b)

同类型的地址也不能直接相加

var a, b *int
a, b = new(int), new(int)
c:=a+b  //invalid operation: a + b (operator + not defined on pointer)

同类型的地址也不能直接相加

var a, b *int
a, b = new(int), new(int)
c := unsafe.Pointer(a) + unsafe.Pointer(b) // invalid operation: unsafe.Pointer(a) + unsafe.Pointer(b) (operator + not defined on unsafe.Pointer)

结合uintptr来做地址之间的运算

var a, b *int
a, b = new(int), new(int)
c := uintptr(unsafe.Pointer(a)) + uintptr(unsafe.Pointer(b))
fmt.Println(c)

unsafe和uintptr的一些组合操作

type User struct {
   Name string
   Age  int8
}
func main() {
   u := &User{}
   uAddress := unsafe.Pointer(u)
   ageOffset := unsafe.Offsetof(u.Age)
   agePtr := unsafe.Pointer(uintptr(uAddress) + ageOffset)
   *((*int)(agePtr)) = 10
   fmt.Println(u.Age)
}
  1. 通过unsafe.Pointer获取u的地址uAddress
  2. 通过unsafe.Offsetof获取u.age的偏移量
  3. uintptr转换uAddress 加上age的偏移量就找到了age的地址agePtr
  4. agePtr转成指针型int,然后取其内存空间,赋值10 结合unsafe和uintptr也可以对某些变量赋值,整个流程看下来比较复杂。

总结

  1. 任何类型的指针都可以被转化为Pointer
  2. Pointer可以被转化为任何类型的指针
  3. uintptr可以被转化为Pointer
  4. Pointer可以被转化为uintptr
  5. unsafe.pointer是普通指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值,必须转换到某一类型的普通指针。
  6. uintptr用于指针运算,GC不把uintptr当指针,uintptr无法持有对象,uintptr类型的目标会被回收。