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

Memory 为什么结构中“[0]字节”的位置很重要?,memory,go,struct,padding,sizeof,Memory,Go,Struct,Padding,Sizeof,golang中的[0]字节不应占用任何内存空间。但这两个结构的大小不同 type bar2 struct { A int _ [0]byte } type bar3 struct { _ [0]byte A int } 那么为什么[0]字节的位置在这里很重要呢 顺便说一下,我使用unsafe.Sizeof()方法检查结构大小。参见。原因是“孔”: 孔是编译器添加的未使用的空格,以确保 以下字段或元素相对于起点正确对齐 结构或数组[1]的 例如(数字基于g

golang中的
[0]字节
不应占用任何内存空间。但这两个结构的大小不同

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

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

顺便说一下,我使用
unsafe.Sizeof()
方法检查结构大小。参见。

原因是“孔”:

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

例如(数字基于go游乐场使用的任何硬件):

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

  • 返回所需的对齐方式
  • 计算字段相对于其封闭结构(包括孔)起点的偏移量
[1] 2016年,北卡罗来纳州克尼汉多诺万第354页。围棋编程语言。纽约第一版:艾迪生·韦斯利。

这是由于一个棘手的填充

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

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

type bar2 struct {
    I int
    A [0]byte
}
当然,这不会改变尺寸和偏移,可以通过以下方式进行验证:

类型为
[0]byte
的值的大小为零,因此在
bar1
中完全可以不为第一个字段(
bar1.a
)保留任何空间,并以0偏移量布置
bar1.I
字段

问题是:为什么编译器不能在第二种情况下(使用
bar2
)执行相同的操作

字段的地址必须位于为前一个字段保留的内存区域之后。在第一种情况下,第一个字段
bar1.A
的大小为0,因此第二个字段的偏移量可能为0,它不会与第一个字段“重叠”

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

这似乎还可以。但是既然
bar2.A
的大小为零,为什么结构
bar2
的大小不能是4字节(或64位拱形中的8字节)

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

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

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

为了避免这种情况,编译器会插入一个填充(根据拱的不同,它将是4或8个字节),这样获取
bar2.a的地址将不会导致地址超出结构的内存,否则可能会引发问题并导致垃圾收集问题(例如,如果只保留
bar2.A
的地址,而不保留结构或指向该结构或其其他字段的另一个指针,则不应对整个结构进行垃圾收集,但由于没有指针指向其内存区域,因此这样做似乎是有效的)。插入的填充将为4(或8)字节,因为

对于结构类型的变量
x
unsafe.Alignof(x)
unsafe.Alignof(x.f)
的每个字段
f
,但至少
1

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

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

type bar2 struct {
    A [0]byte
    I int
    X int
}
事实上,它们在32位arch上都有8个字节(在64位arch上有16个字节)(请在上尝试):


请参阅相关问题:

大小是什么?bar2:8的大小,bar3:4的大小
bool
使用内存空间,这与
[0]字节
不同。如果将bar2和bar3中的
[0]字节
替换为
bool
,两个结构将具有相同的大小。这里有一个完整的示例:是,但需要对齐[0]字节为1。您可以使用
unsafe.Alignof
检查所需的对齐方式,并使用
unsafe.Offsetof
计算字段的偏移量。查看是否打印
bool
[0]的
Alignof
offset
byte
成员,您会发现它们对于两种不同的类型是相同的。因此,这无法解释结构大小的差异。请参阅
bar1 size:     4
bar1.A offset: 0
bar1.I offset: 0

bar2 size:     8
bar2.I offset: 0
bar2.A offset: 4
type bar1 struct {
    I int
    A [0]byte
    X int
}

type bar2 struct {
    A [0]byte
    I int
    X int
}
bar1 size:     8
bar1.I offset: 0
bar1.A offset: 4
bar1.X offset: 4

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