为什么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:对齐很重要。

其他回答

C99 N1256标准草案

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4操作员尺寸:

3当应用于具有结构或并集类型的操作数时,结果是这样的对象中的字节总数,包括内部和尾部衬垫。

6.7.2.1结构和接头规范:

13 ... 可能有未命名的在结构对象内填充,但不能在其开头填充。

and:

15在结构或联合体的末端可能有未命名的填充。

新的C99灵活数组成员特性(结构S{int is[];};)也可能影响填充:

16作为一种特殊情况,具有多个命名构件的结构的最后一个元素可以具有不完整的数组类型;这称为灵活阵列成员。在大多数情况下,柔性阵列成员被忽略。特别是,结构的大小似乎省略了灵活的数组成员,除了它可能具有比这一遗漏意味着。

附件J便携性问题重申:

以下未指定:。。。在结构或联合中存储值时填充字节的值(6.2.6.1)

C++11 N3337标准草案

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3尺寸:

2应用时对于类,结果是该类的对象中的字节数,包括将该类型的对象放置在数组中。

9.2班级成员:

指向标准布局结构对象的指针(使用reinterpret_cast进行适当转换)指向其初始成员(或者如果该成员是位字段,则返回到它所在的单元),反之亦然。[注:因此,在标准布局结构对象中可能存在未命名的填充,必要时实现适当的对齐。-尾注]

我只知道足够的C++来理解注释:-)

上面给出了很多信息(解释)。

我只想分享一些方法来解决这个问题。

您可以通过添加pragma pack来避免它

#pragma pack(push, 1)

// your structure

#pragma pack(pop) 

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

例如,如果您希望结构具有GCC的特定大小,请使用__attribute__((打包))。

在Windows上,使用带有/Zp选项的cl.exe编译器时,可以将对齐设置为一个字节。

通常,CPU更容易访问4(或8)的倍数的数据,这取决于平台和编译器。

所以这基本上是一个对齐问题。

你需要有充分的理由来改变它。

这可能是由于字节对齐和填充,使得结构在您的平台上达到偶数字节(或单词)。例如,在Linux上的C中,以下3种结构:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

成员的大小(以字节为单位)分别为4字节(32位)、8字节(2x 32位)和1字节(2+6位)。上面的程序(在使用gcc的Linux上)将大小打印为4、8和4,其中最后一个结构填充为一个单词(在我的32位平台上为4 x 8位字节)。

oneInt=4
twoInts=8
someBits=4