为什么结构中“[0] byte”的位置很重要?

时间:2017-07-12 03:46:14

标签: memory go struct padding sizeof

golang中的

df = df.rolling(5).sum().diff().shift(-5) print (df) Open High Low Close Volume r_c Date 2017-06-01 41000.0 77000.0 60000.0 71000.0 33891.0 NaN 2017-06-02 18000.0 -18000.0 7000.0 -29000.0 -30713.0 -0.044266 2017-06-05 -45000.0 -37000.0 -29000.0 -27000.0 16768.0 0.000876 2017-06-07 -35000.0 -22000.0 0.0 3000.0 -48895.0 0.013050 2017-06-08 34000.0 17000.0 9000.0 26000.0 -91274.0 0.010146 2017-06-09 -9000.0 -25000.0 -7000.0 -26000.0 73352.0 -0.023004 2017-06-12 8000.0 48000.0 21000.0 59000.0 -4643.0 0.037119 2017-06-13 NaN NaN NaN NaN NaN NaN 2017-06-14 NaN NaN NaN NaN NaN NaN 2017-06-15 NaN NaN NaN NaN NaN NaN 2017-06-16 NaN NaN NaN NaN NaN NaN 2017-06-19 NaN NaN NaN NaN NaN NaN 不应占用任何内存空间。但这两种结构有不同的尺寸。

[0]byte

那么为什么type bar2 struct { A int _ [0]byte } type bar3 struct { _ [0]byte A int } 的位置在这里很重要?

顺便说一句,我使用[0]byte方法来检查结构大小。请参阅full example

2 个答案:

答案 0 :(得分:5)

这是由于棘手的填充。

首先请允许我稍微重命名结构和字段,以便更容易讨论它们:

type bar1 struct {
    A [0]byte
    I int
}

type bar2 struct {
    I int
    A [0]byte
}

这当然不会改变Go Playground上可以验证的尺寸和偏移量:

bar1 size:     4
bar1.A offset: 0
bar1.I offset: 0

bar2 size:     8
bar2.I offset: 0
bar2.A offset: 4

类型[0]byte的值的大小为零,因此在bar1中完全无效,不为第一个字段(bar1.A)保留任何空间,并将其布局为{0}的bar1.I字段。

问题是:为什么编译器在第二种情况下(使用bar2)也能做同样的事情?

字段必须具有必须位于为前一字段保留的存储区之后的地址。在第一种情况下,第一个字段bar1.A具有0个大小,因此第二个字段可以具有0个偏移量,它不会“#34”重叠"第一场。

bar2的情况下,第二个字段不能具有与第一个字段重叠的地址(因此偏移量),因此其偏移量不能小于int的大小,即4在32位体系结构的情况下为字节(在64位拱的情况下为8字节)。

这似乎还不错。但由于bar2.A的大小为零,为什么结构bar2的大小只能是:4个字节(或64位拱形中的8个)?

这是因为获取0大小的字段(和变量)的地址是完全有效的。好的,那又怎么样?

bar2的情况下,编译器必须插入4(或8)字节填充,否则取bar2.A字段的地址会将指向存储区域之外保留为bar2类型的值。

例如,无填充值为bar2的地址可能为0x100,大小为4,因此为struct值保留的内存的地址范围为{{ 1}}。 0x100 .. 0x103的地址为bar2.A,位于结构内存之外。如果是这个结构的数组(例如0x104),如果数组从x [5]bar2开始,0x100的地址将是x[0],地址为0x100将是x[0].A,后续元素0x104的地址也将是x[1],但这是另一个结构值的地址!不酷。

为避免这种情况,编译器会插入一个填充(根据拱形将为4或8个字节),因此取0x104的地址不会导致地址超出结构的范围。 ; s内存,否则可能引发问题并引起有关垃圾收集的问题(例如,如果只保留bar2.A的地址但不保留结构或其他指向它或其他字段的指针,整个结构不应该被垃圾收集,但由于没有指针指向其内存区域,所以这似乎是有效的)。插入的填充将是4(或8)个字节,因为Spec: Size and alignment guarantees:

  

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

如果是这样,添加额外的1字段会使两个结构的大小相等:

int

确实,他们在32位拱门上都有8个字节(在64位拱门上有16个字节)(在Go Playground上试试):

type bar1 struct {
    I int
    A [0]byte
    X int
}

type bar2 struct {
    A [0]byte
    I int
    X int
}

请参阅相关问题:Struct has different size if the field order is different

答案 1 :(得分:1)

原因是“漏洞”:

  

孔是编译器添加的未使用空间,以确保   以下字段或元素相对于开头正确对齐   结构或数组[1]

例如(基于操场上使用的任何硬件的数字):

struct {bool; float64; int16} // 24 bytes
struct {float64; bool; int16} // 16 bytes

您可以使用以下方法验证结构的布局:

[1] p354 Donovan,Kernighan,AD,BK,2016。GO编程语言。第1版。纽约:Addison-Wesley。