Sizeof

Sizeof函数可以返回一个类型所占用的内存大小,这个大小只有类型有关,和类型对应的变量存储的内容大小无关

  1. package main
  2. import (
  3. "fmt"
  4. "unsafe"
  5. )
  6. type bar struct {
  7. a int
  8. b string
  9. }
  10. type foo struct {
  11. a string
  12. b int
  13. c bool
  14. d string
  15. }
  16. func main() {
  17. fmt.Printf("string size: %d\n", unsafe.Sizeof("hello"))
  18. fmt.Printf("string size: %d\n", unsafe.Sizeof("hello go"))
  19. fmt.Printf("byte size: %d\n", unsafe.Sizeof(byte('h')))
  20. fmt.Printf("bool size: %d\n", unsafe.Sizeof(true))
  21. fmt.Printf("int8 size: %d\n", unsafe.Sizeof(int8(0)))
  22. fmt.Printf("int size: %d\n", unsafe.Sizeof(1))
  23. fmt.Printf("int32 size: %d\n", unsafe.Sizeof(int32(2)))
  24. fmt.Printf("int64 size: %d\n", unsafe.Sizeof(int64(3)))
  25. fmt.Printf("map size: %d\n", unsafe.Sizeof(map[string]int{"hello": 1}))
  26. fmt.Printf("list size: %d\n", unsafe.Sizeof([3]int{1, 2, 3}))
  27. fmt.Printf("list size: %d\n", unsafe.Sizeof([2]int{1, 2}))
  28. fmt.Printf("slice size: %d\n", unsafe.Sizeof([]int{1, 2, 3}))
  29. fmt.Printf("slice size: %d\n", unsafe.Sizeof([]int{1}))
  30. var s interface{}
  31. fmt.Printf("interface size: %d\n", unsafe.Sizeof(s))
  32. b := bar{a: 1, b: "2"}
  33. fmt.Printf("bar struct size: %d\n", unsafe.Sizeof(b))
  34. fmt.Printf("bar point size: %d\n", unsafe.Sizeof(&b))
  35. f := foo{a: "hello", b: 2, c: true, d: "world"}
  36. fmt.Printf("foo struct size: %d\n", unsafe.Sizeof(f))
  37. fmt.Printf("foo point size: %d\n", unsafe.Sizeof(&f))
  38. t := struct{}{}
  39. fmt.Printf("empty point size: %d\n", unsafe.Sizeof(t))
  40. }

程序执行结果:

  1. string size: 16
  2. string size: 16
  3. byte size: 1
  4. bool size: 1
  5. int8 size: 1
  6. int size: 8
  7. int32 size: 4
  8. int64 size: 8
  9. map size: 8
  10. list size: 24
  11. list size: 16
  12. slice size: 24
  13. slice size: 24
  14. interface size: 16
  15. bar struct size: 24
  16. bar point size: 8
  17. foo struct size: 48
  18. foo point size: 8
  19. empty point size: 0

平台有关的int类型,这个要看平台是32位还是64位,会取最大的。比如我自己测试,以上输出,会发现int和int64的大小是一样的,因为我的是64位平台的电脑。

  1. func Sizeof(x ArbitraryType) uintptr

以上是Sizeof的函数定义,它接收一个ArbitraryType类型的参数,返回一个uintptr类型的值。这里的ArbitraryType不用关心,他只是一个占位符,为了文档的考虑导出了该类型,但是一般不会使用它,我们只需要知道它表示任何类型,也就是我们这个函数可以接收任意类型的数据。

Alignof

Alignof返回一个类型的对齐值,也可以叫做对齐系数或者对齐倍数。对齐值是一个和内存对齐有关的值

  1. package main
  2. import (
  3. "fmt"
  4. "unsafe"
  5. )
  6. type bar struct {
  7. a int
  8. b string
  9. }
  10. type foo struct {
  11. a string
  12. b int
  13. c bool
  14. d string
  15. }
  16. func main() {
  17. fmt.Printf("string size: %d\n", unsafe.Alignof("hello"))
  18. fmt.Printf("string size: %d\n", unsafe.Alignof("hello go"))
  19. fmt.Printf("byte size: %d\n", unsafe.Alignof(byte('h')))
  20. fmt.Printf("bool size: %d\n", unsafe.Alignof(true))
  21. fmt.Printf("int8 size: %d\n", unsafe.Alignof(int8(0)))
  22. fmt.Printf("int size: %d\n", unsafe.Alignof(1))
  23. fmt.Printf("int32 size: %d\n", unsafe.Alignof(int32(2)))
  24. fmt.Printf("int64 size: %d\n", unsafe.Alignof(int64(3)))
  25. fmt.Printf("map size: %d\n", unsafe.Alignof(map[string]int{"hello": 1}))
  26. fmt.Printf("list size: %d\n", unsafe.Alignof([3]int{1, 2, 3}))
  27. fmt.Printf("list size: %d\n", unsafe.Alignof([2]int{1, 2}))
  28. fmt.Printf("slice size: %d\n", unsafe.Alignof([]int{1, 2, 3}))
  29. fmt.Printf("slice size: %d\n", unsafe.Alignof([]int{1}))
  30. var s interface{}
  31. fmt.Printf("interface size: %d\n", unsafe.Alignof(s))
  32. b := bar{a: 1, b: "2"}
  33. fmt.Printf("bar struct size: %d\n", unsafe.Alignof(b))
  34. fmt.Printf("bar point size: %d\n", unsafe.Alignof(&b))
  35. f := foo{a: "hello", b: 2, c: true, d: "world"}
  36. fmt.Printf("foo struct size: %d\n", unsafe.Alignof(f))
  37. fmt.Printf("foo point size: %d\n", unsafe.Alignof(&f))
  38. t := struct{}{}
  39. fmt.Printf("empty point size: %d\n", unsafe.Alignof(t))
  40. }

打印结果

  1. string size: 8
  2. string size: 8
  3. byte size: 1
  4. bool size: 1
  5. int8 size: 1
  6. int size: 8
  7. int32 size: 4
  8. int64 size: 8
  9. map size: 8
  10. list size: 8
  11. list size: 8
  12. slice size: 8
  13. slice size: 8
  14. interface size: 8
  15. bar struct size: 8
  16. bar point size: 8
  17. foo struct size: 8
  18. foo point size: 8
  19. empty point size: 1

此外,获取对齐值还可以使用反射包的函数,也就是说:unsafe.Alignof(x)等价于reflect.TypeOf(x).Align()
下面函数执行结果和上面是一样的

  1. fmt.Println("bool=",reflect.TypeOf(b).Align())
  2. fmt.Println("int8=",reflect.TypeOf(i8).Align())
  3. fmt.Println("int16=",reflect.TypeOf(i16).Align())
  4. fmt.Println("int32=",reflect.TypeOf(i32).Align())
  5. fmt.Println("int64=",reflect.TypeOf(i64).Align())
  6. fmt.Println("float32=",reflect.TypeOf(f32).Align())
  7. fmt.Println("string=",reflect.TypeOf(s).Align())
  8. fmt.Println("map=",reflect.TypeOf(m).Align())
  9. fmt.Println("list=",reflect.TypeOf(l).Align())
  10. fmt.Println("slice=",reflect.TypeOf(sli).Align())
  11. fmt.Println("point=",reflect.TypeOf(&p).Align())

Offsetof

字段的偏移量,就是该字段在struct结构体内存布局中的起始位置(内存位置索引从0开始)

  1. package main
  2. import (
  3. "fmt"
  4. "unsafe"
  5. )
  6. type user struct {
  7. b byte
  8. i int32
  9. s string
  10. }
  11. func main() {
  12. var u user
  13. fmt.Println("b=",unsafe.Offsetof(u.b))
  14. fmt.Println("i=",unsafe.Offsetof(u.i))
  15. fmt.Println("s=",unsafe.Offsetof(u.s))
  16. }

执行结果

  1. b= 0
  2. i= 4
  3. s= 8

内存对齐

  1. package main
  2. import (
  3. "fmt"
  4. "unsafe"
  5. )
  6. type user1 struct {
  7. b byte
  8. i int32
  9. j string
  10. }
  11. type user2 struct {
  12. b byte
  13. j string
  14. i int32
  15. }
  16. func main() {
  17. var u1 user1
  18. var u2 user2
  19. fmt.Println("u1 offset is ",unsafe.Offsetof(u1.b))
  20. fmt.Println("u1 offset is ",unsafe.Offsetof(u1.i))
  21. fmt.Println("u1 offset is ",unsafe.Offsetof(u1.j))
  22. fmt.Println("u2 offset is ",unsafe.Offsetof(u2.b))
  23. fmt.Println("u2 offset is ",unsafe.Offsetof(u2.j))
  24. fmt.Println("u2 offset is ",unsafe.Offsetof(u2.i))
  25. fmt.Println("u1 size is ",unsafe.Sizeof(u1))
  26. fmt.Println("u2 size is ",unsafe.Sizeof(u2))
  27. }

执行结果

  1. u1 byte offset is 0
  2. u1 int32 offset is 4
  3. u1 string offset is 8
  4. u2 byte offset is 0
  5. u2 string offset is 8
  6. u2 int32 offset is 24
  7. u1 size is 24
  8. u2 size is 32

Pointer

可以假装认为跟C语言的(*void)差不多

  1. 任何指针都可以转换为unsafe.Pointer
  2. unsafe.Pointer可以转换为任何指针
  3. uintptr可以转换为unsafe.Pointer
  4. unsafe.Pointer不参与指针运算,如果非要这么干将unsafe.Pointer
  1. package main
  2. import (
  3. "fmt"
  4. "unsafe"
  5. )
  6. type user struct {
  7. name string
  8. age int
  9. }
  10. func main() {
  11. u:=user{
  12. name: "张三",
  13. age: 10,
  14. }
  15. fmt.Println(u)
  16. pName:=(*string)(unsafe.Pointer(&u))
  17. *pName="李四"
  18. pAge:=(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&u))+unsafe.Offsetof(u.age)))
  19. *pAge = 20
  20. fmt.Println(u)
  21. }

uinptr

为了刻意的进行指针运算可以将unsafe.Pointer可以转换为uintptr