结构中字段对齐的奇怪行为

Posted

技术标签:

【中文标题】结构中字段对齐的奇怪行为【英文标题】:Strange behavior of fields aligment in the structure 【发布时间】:2015-01-13 20:18:36 【问题描述】:
#include <iostream>
using namespace std;
#pragma pack(push, 4)
struct Foo

    char ch;   //1
    char ch2;  //1
    char ch3;  //1
    char ch4;  //1 _4
    char ch5;  //1
    short num; //2
    char ch6;  //1 _4
    int num2;  //4 _4
;
#pragma pack(pop)

int main() 
    cout << sizeof( Foo );
    return 0;

为什么输出是 16 字节?我认为它必须是 12,因为:

4 char = 4 bytes  
char + short + char = 4 bytes  
int = 4 bytes  

那么有人能解释一下剩下的 4 个字节在哪里吗?

【问题讨论】:

short 不仅在您的实现中是 2 大,它还具有对齐 2。 【参考方案1】:

你得到的是内存中的以下内容(|s 是 4 字节边界):

|char char char char|char 1bytePadding short|char 3bytesPadding|int|

short需要对齐到2byte的边界,所以在前面的char后面插入一个字节的padding就可以了。同样int必须是4字节对齐的,所以char之后必须插入3字节的填充,这样就可以了。如果您正在优化空间,则经验法则是将成员从大到小排序。如果你这样做了,那将是:

|int|short char char| char char char char|

这将占用 12 个字节,如您所料。

【讨论】:

【参考方案2】:

这与对齐有关。

简而言之,CPU 更喜欢不同的值在内存中“对齐”。例如,如果您正在处理一个标准的 32 位整数(4 个字节),大多数 CPU 都希望它驻留在可被 4 整除的内存地址中。因此,10004 的内存地址就可以了,并且10008 可以,但10005 不行。

大多数 CPU 在处理未对齐的值时会抛出异常并拒绝处理。然而,我们值得信赖的 x86 是一个例外,并且会正确处理它——尽管速度要慢得多。在幕后,它将从内存中获取 2 个对齐的整数,然后旋转这些位以从中提取未对齐的整数。 (在其他平台上,我认为编译器会生成额外的指令来完成这项工作,但我不确定)所以你真的不希望这种情况发生,除非你有充分的理由为它。

这就是为什么您的编译器会在 struct 成员之间生成一些填充字节 - 以便 short 位于偶数地址中,而 int 将位于可被 4 整除的地址中。

#pragma pack 会影响这一点,但前提是您将其设置为小于 4。你会得到我之前提到的对齐问题。

【讨论】:

"会正确处理吗"?只有有时,原子性会被打破。 确实是这样,没想到。【参考方案3】:

这是带有偏移的结构:

struct Foo

    0:  char ch;   //1
    1:  char ch2;  //1
    2:  char ch3;  //1
    3:  char ch4;  //1 
    4:  char ch5;  //1
    5:  _padding   //1
    6:  short num; //2
    8:  char ch6;  //1 
    9:  _padding   //3
    12: int num2;  //4 
    16:
;

由于您平台上的short 有2 个字节对齐,因此在num 之前添加了1 个字节填充,使其偏移量可被2 整除。然后ch6 之后有3 个字节使num2 的偏移量可除以4.

【讨论】:

以上是关于结构中字段对齐的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

添加到 std::vector 时类字段的奇怪行为

LLVM IR 对结构内存对齐的有线行为?

单击 Chrome 和 Safari 中的输入字段后的奇怪行为

C中的结构内存布局

在GoLang中使用嵌套结构打印struct的奇怪行为

iOS 5:在 TextField 之间跳转时出现奇怪的行为