Sizeof
Sizeof
函数可以返回一个类型所占用的内存大小,这个大小只有类型有关,和类型对应的变量存储的内容大小无关
package main
import (
"fmt"
"unsafe"
)
type bar struct {
a int
b string
}
type foo struct {
a string
b int
c bool
d 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: 16
string size: 16
byte size: 1
bool size: 1
int8 size: 1
int size: 8
int32 size: 4
int64 size: 8
map size: 8
list size: 24
list size: 16
slice size: 24
slice size: 24
interface size: 16
bar struct size: 24
bar point size: 8
foo struct size: 48
foo point size: 8
empty point size: 0
平台有关的int类型,这个要看平台是32位还是64位,会取最大的。比如我自己测试,以上输出,会发现int和int64的大小是一样的,因为我的是64位平台的电脑。
func Sizeof(x ArbitraryType) uintptr
以上是Sizeof
的函数定义,它接收一个ArbitraryType
类型的参数,返回一个uintptr
类型的值。这里的ArbitraryType
不用关心,他只是一个占位符,为了文档的考虑导出了该类型,但是一般不会使用它,我们只需要知道它表示任何类型,也就是我们这个函数可以接收任意类型的数据。
Alignof
Alignof
返回一个类型的对齐值,也可以叫做对齐系数或者对齐倍数。对齐值是一个和内存对齐有关的值
package main
import (
"fmt"
"unsafe"
)
type bar struct {
a int
b string
}
type foo struct {
a string
b int
c bool
d 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: 8
string size: 8
byte size: 1
bool size: 1
int8 size: 1
int size: 8
int32 size: 4
int64 size: 8
map size: 8
list size: 8
list size: 8
slice size: 8
slice size: 8
interface size: 8
bar struct size: 8
bar point size: 8
foo struct size: 8
foo point size: 8
empty 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 main
import (
"fmt"
"unsafe"
)
type user struct {
b byte
i int32
s string
}
func main() {
var u user
fmt.Println("b=",unsafe.Offsetof(u.b))
fmt.Println("i=",unsafe.Offsetof(u.i))
fmt.Println("s=",unsafe.Offsetof(u.s))
}
执行结果
b= 0
i= 4
s= 8
内存对齐
package main
import (
"fmt"
"unsafe"
)
type user1 struct {
b byte
i int32
j string
}
type user2 struct {
b byte
j string
i int32
}
func main() {
var u1 user1
var u2 user2
fmt.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 0
u1 int32 offset is 4
u1 string offset is 8
u2 byte offset is 0
u2 string offset is 8
u2 int32 offset is 24
u1 size is 24
u2 size is 32
Pointer
可以假装认为跟C语言的(*void)差不多
- 任何指针都可以转换为unsafe.Pointer
- unsafe.Pointer可以转换为任何指针
- uintptr可以转换为unsafe.Pointer
- unsafe.Pointer不参与指针运算,如果非要这么干将unsafe.Pointer
package main
import (
"fmt"
"unsafe"
)
type user struct {
name string
age 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 = 20
fmt.Println(u)
}
uinptr
为了刻意的进行指针运算可以将unsafe.Pointer可以转换为uintptr
- 一个足够大的无符号整型, 用来表示任意地址,可以进行数值计算。
- GC 不把 uintptr 当指针,uintptr 无法持有对象
参考
https://github.com/jinhailang/blog/issues/43