填充&am​​p;对齐 - 误解

时间:2014-03-01 05:53:17

标签: c++ c

鉴于以下非常简单的结构:

struct A {

    int a;
    double b;

};

(使用Mac OS 10.9 - Xcode)

结构的大小是16.我不明白为什么。为什么不是12? 12是4的倍数。 我在这里缺少什么?

谢谢

编辑:

struct B {

int a;
char b[4];
char c[4];
};

此结构的大小为12.因此,这意味着它是合格的大小。 为什么编译器会用另外4个字节填充第一个结构?

4 个答案:

答案 0 :(得分:3)

编译器和体系结构可能更喜欢double b的地址在sizeof(double) == 8之后的8字节边界上对齐,就像它更喜欢int a的地址在4字节上对齐边界。

因此,编译器可能在“int a”和“double b”之间插入4个字节的“填充”,这样两个变量就可以在预期的边界上排列。

大多数编译器通常提供关闭填充的方法(对网络/磁盘序列化很有用),但它依赖于编译器。在Windows上它是#pragma pack(1)。在gcc上__attribute__ ((packed))不确定Mac / Clang是否具有相似的属性。

答案 1 :(得分:2)

4不是幻数。对齐通常是对标量对象的大小进行的,在这种情况下,double为8。但这不是一个严格的规则。机器或平台可以规定它想要的任何对齐要求。

请参阅ABI spec了解完整(阅读:令人头脑麻木)的详细信息。

答案 2 :(得分:0)

编译器需要添加填充以确保结构的每个成员的地址应根据其类型进行对齐。

在这种情况下:

struct A {
    int a;
    double b;

};

假设sizeof(int)为4,sizeof(double)为8,则a的地址应为4的倍数,b的地址为8的倍数。 (这称为自然对齐。)如果此类型的变量的基址为零,则要使此变量的成员b的地址满足其对齐要求,编译器需要添加4字节填充在成员a之后。

答案 3 :(得分:0)

在第一种情况下添加了填充,因为在您的计算机上,double与8个字节对齐。所以它必须驻留在一个可以被8整除的地址。

0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 

a    a    a    a    |     padding     | b    b    b    b    b    b    b    b

如果未添加填充,则double成员从地址0x04开始,这是错误的。添加了4个填充字节。现在double位于0x08。 因此,大小是16。

对于第二种情况:

struct B {

 int a;
 char b[4];
 char c[4];
};

0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 
a    a    a    a    b[0] b[1] b[2] b[3] c[0] c[1] c[2] c[3]

在这种情况下,如果基地址再次为0,则不需要填充,因为所有都已对齐。

试试这个:

struct B {
 char b[5];
 int a;
 char c[4];
};

,大小为16个字节。添加填充以使int成员在可被4整除的地址处开始。

0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
b[0] b[1] b[2] b[3] b[4] |   padding  | a    a    a    a    c[0] c[1] c[2] c[3]