Sizeof
Sizeof函数可以返回一个类型所占用的内存大小,这个大小只有类型有关,和类型对应的变量存储的内容大小无关
package mainimport ("fmt""unsafe")type bar struct {a intb string}type foo struct {a stringb intc boold string}func main() {fmt.Printf("string size: %d\n", unsafe.Sizeof("hello"))fmt.Printf("string size: %d\n", unsafe.Sizeof("hello go"))fmt.Printf("byte size: %d\n", unsafe.Sizeof(byte('h')))fmt.Printf("bool size: %d\n", unsafe.Sizeof(true))fmt.Printf("int8 size: %d\n", unsafe.Sizeof(int8(0)))fmt.Printf("int size: %d\n", unsafe.Sizeof(1))fmt.Printf("int32 size: %d\n", unsafe.Sizeof(int32(2)))fmt.Printf("int64 size: %d\n", unsafe.Sizeof(int64(3)))fmt.Printf("map size: %d\n", unsafe.Sizeof(map[string]int{"hello": 1}))fmt.Printf("list size: %d\n", unsafe.Sizeof([3]int{1, 2, 3}))fmt.Printf("list size: %d\n", unsafe.Sizeof([2]int{1, 2}))fmt.Printf("slice size: %d\n", unsafe.Sizeof([]int{1, 2, 3}))fmt.Printf("slice size: %d\n", unsafe.Sizeof([]int{1}))var s interface{}fmt.Printf("interface size: %d\n", unsafe.Sizeof(s))b := bar{a: 1, b: "2"}fmt.Printf("bar struct size: %d\n", unsafe.Sizeof(b))fmt.Printf("bar point size: %d\n", unsafe.Sizeof(&b))f := foo{a: "hello", b: 2, c: true, d: "world"}fmt.Printf("foo struct size: %d\n", unsafe.Sizeof(f))fmt.Printf("foo point size: %d\n", unsafe.Sizeof(&f))t := struct{}{}fmt.Printf("empty point size: %d\n", unsafe.Sizeof(t))}
程序执行结果:
string size: 16string size: 16byte size: 1bool size: 1int8 size: 1int size: 8int32 size: 4int64 size: 8map size: 8list size: 24list size: 16slice size: 24slice size: 24interface size: 16bar struct size: 24bar point size: 8foo struct size: 48foo point size: 8empty point size: 0
平台有关的int类型,这个要看平台是32位还是64位,会取最大的。比如我自己测试,以上输出,会发现int和int64的大小是一样的,因为我的是64位平台的电脑。
func Sizeof(x ArbitraryType) uintptr
以上是Sizeof的函数定义,它接收一个ArbitraryType类型的参数,返回一个uintptr类型的值。这里的ArbitraryType不用关心,他只是一个占位符,为了文档的考虑导出了该类型,但是一般不会使用它,我们只需要知道它表示任何类型,也就是我们这个函数可以接收任意类型的数据。
Alignof
Alignof返回一个类型的对齐值,也可以叫做对齐系数或者对齐倍数。对齐值是一个和内存对齐有关的值
package mainimport ("fmt""unsafe")type bar struct {a intb string}type foo struct {a stringb intc boold string}func main() {fmt.Printf("string size: %d\n", unsafe.Alignof("hello"))fmt.Printf("string size: %d\n", unsafe.Alignof("hello go"))fmt.Printf("byte size: %d\n", unsafe.Alignof(byte('h')))fmt.Printf("bool size: %d\n", unsafe.Alignof(true))fmt.Printf("int8 size: %d\n", unsafe.Alignof(int8(0)))fmt.Printf("int size: %d\n", unsafe.Alignof(1))fmt.Printf("int32 size: %d\n", unsafe.Alignof(int32(2)))fmt.Printf("int64 size: %d\n", unsafe.Alignof(int64(3)))fmt.Printf("map size: %d\n", unsafe.Alignof(map[string]int{"hello": 1}))fmt.Printf("list size: %d\n", unsafe.Alignof([3]int{1, 2, 3}))fmt.Printf("list size: %d\n", unsafe.Alignof([2]int{1, 2}))fmt.Printf("slice size: %d\n", unsafe.Alignof([]int{1, 2, 3}))fmt.Printf("slice size: %d\n", unsafe.Alignof([]int{1}))var s interface{}fmt.Printf("interface size: %d\n", unsafe.Alignof(s))b := bar{a: 1, b: "2"}fmt.Printf("bar struct size: %d\n", unsafe.Alignof(b))fmt.Printf("bar point size: %d\n", unsafe.Alignof(&b))f := foo{a: "hello", b: 2, c: true, d: "world"}fmt.Printf("foo struct size: %d\n", unsafe.Alignof(f))fmt.Printf("foo point size: %d\n", unsafe.Alignof(&f))t := struct{}{}fmt.Printf("empty point size: %d\n", unsafe.Alignof(t))}
打印结果
string size: 8string size: 8byte size: 1bool size: 1int8 size: 1int size: 8int32 size: 4int64 size: 8map size: 8list size: 8list size: 8slice size: 8slice size: 8interface size: 8bar struct size: 8bar point size: 8foo struct size: 8foo point size: 8empty point size: 1
此外,获取对齐值还可以使用反射包的函数,也就是说:unsafe.Alignof(x)等价于reflect.TypeOf(x).Align()。
下面函数执行结果和上面是一样的
fmt.Println("bool=",reflect.TypeOf(b).Align())fmt.Println("int8=",reflect.TypeOf(i8).Align())fmt.Println("int16=",reflect.TypeOf(i16).Align())fmt.Println("int32=",reflect.TypeOf(i32).Align())fmt.Println("int64=",reflect.TypeOf(i64).Align())fmt.Println("float32=",reflect.TypeOf(f32).Align())fmt.Println("string=",reflect.TypeOf(s).Align())fmt.Println("map=",reflect.TypeOf(m).Align())fmt.Println("list=",reflect.TypeOf(l).Align())fmt.Println("slice=",reflect.TypeOf(sli).Align())fmt.Println("point=",reflect.TypeOf(&p).Align())
Offsetof
字段的偏移量,就是该字段在struct结构体内存布局中的起始位置(内存位置索引从0开始)
package mainimport ("fmt""unsafe")type user struct {b bytei int32s string}func main() {var u userfmt.Println("b=",unsafe.Offsetof(u.b))fmt.Println("i=",unsafe.Offsetof(u.i))fmt.Println("s=",unsafe.Offsetof(u.s))}
执行结果
b= 0i= 4s= 8
内存对齐
package mainimport ("fmt""unsafe")type user1 struct {b bytei int32j string}type user2 struct {b bytej stringi int32}func main() {var u1 user1var u2 user2fmt.Println("u1 offset is ",unsafe.Offsetof(u1.b))fmt.Println("u1 offset is ",unsafe.Offsetof(u1.i))fmt.Println("u1 offset is ",unsafe.Offsetof(u1.j))fmt.Println("u2 offset is ",unsafe.Offsetof(u2.b))fmt.Println("u2 offset is ",unsafe.Offsetof(u2.j))fmt.Println("u2 offset is ",unsafe.Offsetof(u2.i))fmt.Println("u1 size is ",unsafe.Sizeof(u1))fmt.Println("u2 size is ",unsafe.Sizeof(u2))}
执行结果
u1 byte offset is 0u1 int32 offset is 4u1 string offset is 8u2 byte offset is 0u2 string offset is 8u2 int32 offset is 24u1 size is 24u2 size is 32
Pointer
可以假装认为跟C语言的(*void)差不多
- 任何指针都可以转换为unsafe.Pointer
- unsafe.Pointer可以转换为任何指针
- uintptr可以转换为unsafe.Pointer
- unsafe.Pointer不参与指针运算,如果非要这么干将unsafe.Pointer
package mainimport ("fmt""unsafe")type user struct {name stringage int}func main() {u:=user{name: "张三",age: 10,}fmt.Println(u)pName:=(*string)(unsafe.Pointer(&u))*pName="李四"pAge:=(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&u))+unsafe.Offsetof(u.age)))*pAge = 20fmt.Println(u)}
uinptr
为了刻意的进行指针运算可以将unsafe.Pointer可以转换为uintptr
- 一个足够大的无符号整型, 用来表示任意地址,可以进行数值计算。
- GC 不把 uintptr 当指针,uintptr 无法持有对象
参考
https://github.com/jinhailang/blog/issues/43
