7

举这个例子

class A
{
public:
  int a; 
  char b;
  int c;
};

我看到的每个编译器(对于 x86、32 或 64 位)都为 class 分配 12 个字节A,而不是 9 个。因此它们b与整数边界或您可以说的总线边界对齐。我的问题是这是否符合 C++ 标准,以及是否有任何编译器不这样做。

4

5 回答 5

17

C++ 标准规定:

  • 对象有一个对齐要求,其大小是倍数(所以如果int是 4 个字节宽,那么它需要 1、2 或 4 个字节的对齐,具体取决于实现)。
  • 成员对象(如果它们没有被诸如 之类的访问说明符分隔public)全部按照它们被声明的顺序分配
  • 并且根据其对齐要求分配它们。

所以不,标准并没有明确说明该类的大小应为 12 个字节。

但是它确实b应该在之后分配a,并且c应该在之后分配b

int4 字节宽且需要 4 字节对齐的平台上,这会留下 12 字节作为最小有效大小:

  • a占用前 4 个字节
  • b占用一个字节
  • c需要 4 个字节,必须在 4 个字节的边界上分配。b在这样的边界之后结束一个字节,因此通过c插入 3 个字节的填充来找到下一个要放置的有效位置。

所以类的总大小最终是成员的大小 (4 + 1 + 4 = 9) 加上三个字节的填充,总共 12。

还有另一条规则在这里无效,但是如果您已经按顺序定义了成员,这将很重要a, c, b

包含类 ( A) 从最严格对齐的成员对象继承对齐要求。也就是说,因为它包含 a int,所以它具有与 a 相同的对齐要求int。而且因为对象的总大小必须是其对齐要求的倍数,包含按顺序排列的成员的类仍然a, b, c需要12 个字节的存储空间。它只是将 3 个字节的填充移到类的末尾,而不是 between and 。bc

但是,在其他一些情况下,按大小降序对成员重新排序有时会减小类的大小。

假设我们有一个这样的类:

class B {
  char a;
  double b;
  int c;
};

这将需要 24 字节的存储空间(1 字节用于a,8 字节用于b,4 字节用于c,但是为了确保b最终在 8 字节边界上,我们需要在和之间填充7字节,并确保整个类最终的大小是 8 的倍数,我们需要另外 4 个字节。abc

但是根据大小重新排序成员,如下所示:

class B {
  double b;
  int c;
  char a;
};

生成一个只需要 16 个字节的类:

成员对象本身相同的 1 + 4 + 8 字节,但现在c已经在 4 字节边界上对齐(因为它之后b以 8 字节边界结束),并且a不需要任何对齐,所以唯一的对齐我们需要的是确保它B的大小是 8 的倍数。成员占用 13 个字节,因此我们可以添加 3 个字节的填充,并且类最终为 16 个字节,比第一个版本小 33%。

于 2012-11-08T14:07:55.437 回答
1

可以在编译时使用 pragma 指令控制结构打包。 请参阅#Pragma 包

例如,以下不会对齐成员,而是将它们彼此相邻放置。

#pragma pack(push, 1)
struct A4
{
  char a;
  double b;
  char c;
};
#pragma pack(pop)

示例来自这里

GCC还支持编译指示包。该指令不是某些标准的一部分,但许多编译器确实支持它。


但是,不应该有这样做的理由。编译器将它们对齐以加快对成员的访问,并且应该没有理由更改它

于 2012-11-08T14:05:18.020 回答
0

是的,标准中到处都提到了对齐,主要是第 3.11 节(对齐)。它依赖于平台,因此任何依赖于对象实际大小的程序本质上都是不可移植的。

于 2012-11-08T14:08:14.423 回答
0

标准允许编译器在必要时填充更多字节。它基本上取决于主机的体系结构而不是编译器。

于 2012-11-08T14:05:11.087 回答
0

在您的情况下,“b”始终正确对齐。它被填充以在 32 位边界中对齐c 。尽管有一些特定实现的空间,但大多数编译器都遵循将 2 和 4 字节变量与 2 和 4 字节边界对齐的规则。

在 32 位系统中,double 和 long int(8 字节)与 4 字节边界对齐,但在 64 位系统中与 8 字节对齐。

于 2012-11-08T14:10:59.863 回答