uintptr是go的内置类型,用于指针运算,其底层基于int类型。uintptr不是指针,GC会回收uintptr类型的对象。
unsafe.Sizeof
unsafe.Sizeof
函数返回的就是uintptr类型的值(表达式,即值的大小):
var p float64 = 99
fmt.Println(reflect.TypeOf(unsafe.Sizeof(p)))
fmt.Println(unsafe.Sizeof(p))
>>> uintptr
>>> 8
unsafe.Sizeof
接受任意类型的值(表达式),返回其占用的字节数,在上面的例子中float64的大小是8bytes。
如果传入一个指针类型的对象会返回多少呢?
type W struct {
a byte
b int32
c int64
}
var w *W
fmt.Println(unsafe.Sizeof(w)) // 4 or 8
一般情况下,可能是4或8,因为w是指针类型uintptr,而uintptr是平台相关的,在32位系统下大小是4bytes,在64位系统下是8bytes。
要获取值类型的大小,需要对指针变量进行取值:
fmt.Println(unsafe.Sizeof(*w))
>>> 16
对齐
在上面的例子中,*w
的大小为16,按照常理来说,byte占用1字节,int32占用4字节,int64占用8字节,大小应该是13才对。这是因为发生了对齐,unsafe.Alignof
可以计算对齐值:
unsafe.Alignof(w.a) // type byte
unsafe.Alignof(w.b) // type int32
unsafe.Alignof(w.c) // type int64
分别是1、4、8,因为int32类型的对齐值是4bytes,必须是4的倍数,故byte类型要填充3个字节。而填充后,两者的大小和为8bytes,int64对齐值是8bytes,不需要填充,所以用unsafe.Sizeof
获取到结构的大小为4+4+8=16
。
反射包的对齐方法
反射包也有某些方法可用于计算对齐值:
unsafe.Alignof(w)
等价于reflect.TypeOf(w).Align
。
unsafe.Alignof(w.i)
等价于reflect.Typeof(w.i).FieldAlign()
。
结构体的对齐值
如果我们计算的是结构体的对齐值而不是某个字段或者基本类型,那么值会是多少呢?
type W struct {
a byte
b int32
c int64
}
var w *W
var w2 W
fmt.Println(unsafe.Alignof(w))
fmt.Println(unsafe.Alignof(w2))
fmt.Println(refelct.TypeOf(w).Elem().Align())
>>> 4
>>> 8
>>> 8
32位机器下,指针对象的对齐值是4,因为指针类型是uintptr。而结构体的值类型却是8bytes的对齐值,这是因为会先进行字段的对齐,字段最大的对齐值是8bytes,因此结构体值类型的对齐值也是8。
更改结构,验证一下:
type W struct {
a byte
b int32
c int32
}
var w W
fmt.Println(unsafe.Alignof(w))
>>> 4
有疑问加站长微信联系(非本文作者)