为什么sizeof运算符返回的结构大小大于结构成员的总大小?


当前回答

其思想是,出于速度和缓存的考虑,操作数应从与其自然大小对齐的地址中读取。为了实现这一点,编译器填充结构成员,以便对齐以下成员或以下结构。

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

x86体系结构始终能够获取未对齐的地址。然而,它速度较慢,当未对齐与两个不同的缓存线重叠时,当对齐的访问只会逐出一个缓存线时,它会逐出两个缓存线。

有些架构实际上必须捕获未对齐的读写,而早期版本的ARM架构(演变成当今所有移动CPU的架构)。。。事实上,他们只是返回了这些错误的数据。(他们忽略了低位。)

最后,请注意缓存线可以任意大,编译器不会试图猜测这些缓存线,也不会做出空间与速度的权衡。相反,对齐决策是ABI的一部分,表示最终将均匀填充缓存行的最小对齐。

TL;DR:对齐很重要。

其他回答

另请参见:

对于Microsoft Visual C:

http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx

GCC声称与微软编译器兼容

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Structure_002dPacking-Pragmas.html

除了前面的答案,请注意,无论包装如何,C++中没有成员订单保证。编译器可以(当然也可以)向结构中添加虚拟表指针和基结构的成员。即使是虚拟表的存在也没有得到标准的保证(没有规定虚拟机制的实现),因此可以得出这样的保证是不可能的。

我很确定C语言保证了成员顺序,但在编写跨平台或跨编译器程序时,我不会指望它。

如果隐式或显式设置了结构的对齐方式,则可以这样做。对齐为4的结构将始终是4字节的倍数,即使其成员的大小不是4字节的倍。

此外,一个库可以在x86下用32位int编译,并且您可以在64位进程上比较它的组件。如果您手动执行此操作,则会得到不同的结果。

在关于内存对齐和结构填充/打包的其他解释得很好的答案中,我通过仔细阅读问题本身发现了一些东西。

“为什么结构的sizeof不等于每个成员的sizeof之和?”“为什么sizeof运算符返回的结构大小大于结构成员的总大小”?

这两个问题都表明了一些明显的错误。至少在一般的、非示例性的视图中是这样的。

应用于结构对象的sizeof操作数的结果可以等于分别应用于每个成员的sizeof之和。它不一定要更大/不同。

如果没有填充的原因,则不会填充内存。


如果结构仅包含相同类型的成员,则大多数实现为:

struct foo {
   int a;   
   int b;
   int c;     
} bar;

假设sizeof(int)==4,结构杆的尺寸将等于所有构件的尺寸总和,sizeof(bar)==12。这里没有填充。

同样的例子如下:

struct foo {
   short int a;   
   short int b;
   int c;     
} bar;

假设sizeof(short int)==2,sizeof(int)==4。为a和b分配的字节之和等于为c分配的字节,c是最大的成员,因此所有内容都完全对齐。因此,sizeof(bar)==8。

这也是关于结构填充的第二个最受欢迎的问题的对象,这里:

C结构中的内存对齐

这是因为添加了填充以满足对齐约束。数据结构对齐会影响程序的性能和正确性:

未对齐的访问可能是一个硬错误(通常是SIGBUS)。未对齐的访问可能是软错误。要么在硬件中进行了纠正,以适度降低性能。或通过软件仿真进行纠正,以严重降低性能。此外,原子性和其他并发性保证可能会被破坏,从而导致微妙的错误。

下面是一个使用x86处理器典型设置的示例(均使用32位和64位模式):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

可以通过对齐对成员进行排序来最小化结构的大小(按大小排序足以满足基本类型的排序)(如上面示例中的结构Z)。

重要提示:C和C++标准都规定结构对齐是由实现定义的。因此,每个编译器可能会选择不同的数据对齐方式,从而导致不同且不兼容的数据布局。因此,在处理不同编译器将使用的库时,了解编译器如何对齐数据很重要。某些编译器具有命令行设置和/或特殊的#pragma语句来更改结构对齐设置。

除了其他答案,结构可以(但通常不)具有虚拟函数,在这种情况下,结构的大小还将包括vtbl的空间。