如果字段顺序不同,则Struct具有不同的大小

时间:2015-12-11 08:40:05

标签: memory struct go padding sizeof

package main

import (
    "fmt"
    "unsafe"
)

type A struct {
    a bool
    b int64
    c int
}

type B struct {
    b int64
    a bool
    c int
}

type C struct {
}

func main() {
    // output 24
    fmt.Println(unsafe.Sizeof(A{}))

    // output 16
    fmt.Println(unsafe.Sizeof(B{}))

    // output 0
    fmt.Println(unsafe.Sizeof(C{}))
}
  1. Struct AB具有相同的字段,但如果以不同的顺序指定,则会产生不同的大小。为什么呢?

  2. struct C的大小为零。系统为a := C{}分配了多少内存?

  3. 感谢。

1 个答案:

答案 0 :(得分:11)

1。结构尺寸

<强> TL; DR; (摘要):如果重新排序字段,将使用不同的隐式填充,隐式填充将计入struct的大小。

请注意,结果取决于目标架构;您发布的结果在GOARCH=386时适用,但在GOARCH=amd64时,A{}B{}的大小将为24字节。

结构的字段地址必须对齐,类型int64的字段地址必须是8字节的倍数。 Spec: Package unsafe:

  

计算机体系结构可能要求内存地址对齐;也就是说,对于变量的地址是因子的倍数,变量的类型对齐。函数Alignof采用表达任何类型变量的表达式,并以字节为单位返回(变量类型)的对齐方式。

int64的对齐是8个字节:

fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8

所以在A的情况下,因为第一个字段是bool,所以在A.a之后有一个7字节的隐式填充,因此A.b类型为int64可以从一个8的倍数开始。这个(确切需要7字节填充)得到保证,因为struct本身与8的倍数对齐,因为那是所有领域中规模最大的。请参阅:Spec: Size alignment guarantees:

  

对于结构类型的变量xunsafe.Alignof(x)unsafe.Alignof(x.f)的每个字段f的所有值x中的最大值,但至少1

如果B(如果GOARCH=386是你的情况),那么在B.a类型的bool字段之后只会有一个3字节的隐式填充,因为此字段后跟一个int类型的字段(大小为4个字节),而不是int64

如果intGOARCH=386的对齐为4个字节,GOARCH=amd64则为8个字节:

fmt.Println(unsafe.Alignof((int(0))))   // Prints 4 if GOARCH=386, and 8 if GOARCH=amd64

使用unsafe.Offsetof()查找字段的偏移量:

// output 24
a := A{}
fmt.Println(unsafe.Sizeof(a),
    unsafe.Offsetof(a.a), unsafe.Offsetof(a.b), unsafe.Offsetof(a.c))

// output 16
b := B{}
fmt.Println(unsafe.Sizeof(b),
    unsafe.Offsetof(b.b), unsafe.Offsetof(b.a), unsafe.Offsetof(b.c))

// output 0
fmt.Println(unsafe.Sizeof(C{}))

var i int
fmt.Println(unsafe.Sizeof(i))

如果GOARCH=386输出(在Go Playground上尝试):

24 0 8 16
16 0 8 12
0
4

输出GOARCH=amd64

24 0 8 16
24 0 8 16
0
8

2。零大小值

Spec: Size alignment guarantees:

  

如果结构或数组类型不包含大小大于零的字段(或元素),则其大小为零。 两个不同的零大小变量在内存中可能具有相同的地址。

因此规范只是提示使用相同的内存地址但不是必需的。但目前的实施遵循它。也就是说,不会为大小为零的类型的值分配内存,这包括空结构struct{}和零长度的数组,例如, [0]int,或其元素大小为零(且任意长度)的数组。

见这个例子:

a := struct{}{}
b := struct{}{}
c := [0]int{}
d := [3]struct{}{}

fmt.Printf("%p %p %p %p %p", &a, &b, &c, &d, &d[2])

输出(在Go Playground上试试):所有地址都相同。

0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c

有关一个有趣且相关的主题,请阅读:Dave Cheney: Padding is hard