举这个例子
class A
{
public:
int a;
char b;
int c;
};
我看到的每个编译器(对于 x86、32 或 64 位)都为 class 分配 12 个字节A
,而不是 9 个。因此它们b
与整数边界或您可以说的总线边界对齐。我的问题是这是否符合 C++ 标准,以及是否有任何编译器不这样做。
C++ 标准规定:
int
是 4 个字节宽,那么它需要 1、2 或 4 个字节的对齐,具体取决于实现)。public
)全部按照它们被声明的顺序分配所以不,标准并没有明确说明该类的大小应为 12 个字节。
但是它确实说b
应该在之后分配a
,并且c
应该在之后分配b
。
在int
4 字节宽且需要 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 。b
c
但是,在其他一些情况下,按大小降序对成员重新排序有时会减小类的大小。
假设我们有一个这样的类:
class B {
char a;
double b;
int c;
};
这将需要 24 字节的存储空间(1 字节用于a
,8 字节用于b
,4 字节用于c
,但是为了确保b
最终在 8 字节边界上,我们需要在和之间填充7字节,并确保整个类最终的大小是 8 的倍数,我们需要另外 4 个字节。a
b
c
但是根据大小重新排序成员,如下所示:
class B {
double b;
int c;
char a;
};
生成一个只需要 16 个字节的类:
成员对象本身相同的 1 + 4 + 8 字节,但现在c
已经在 4 字节边界上对齐(因为它之后b
以 8 字节边界结束),并且a
不需要任何对齐,所以唯一的对齐我们需要的是确保它B
的大小是 8 的倍数。成员占用 13 个字节,因此我们可以添加 3 个字节的填充,并且类最终为 16 个字节,比第一个版本小 33%。
可以在编译时使用 pragma 指令控制结构打包。 请参阅#Pragma 包
例如,以下不会对齐成员,而是将它们彼此相邻放置。
#pragma pack(push, 1)
struct A4
{
char a;
double b;
char c;
};
#pragma pack(pop)
示例来自这里。
GCC还支持编译指示包。该指令不是某些标准的一部分,但许多编译器确实支持它。
但是,不应该有这样做的理由。编译器将它们对齐以加快对成员的访问,并且应该没有理由更改它。
是的,标准中到处都提到了对齐,主要是第 3.11 节(对齐)。它依赖于平台,因此任何依赖于对象实际大小的程序本质上都是不可移植的。
标准允许编译器在必要时填充更多字节。它基本上取决于主机的体系结构而不是编译器。
在您的情况下,“b”始终正确对齐。它被填充以在 32 位边界中对齐c 。尽管有一些特定实现的空间,但大多数编译器都遵循将 2 和 4 字节变量与 2 和 4 字节边界对齐的规则。
在 32 位系统中,double 和 long int(8 字节)与 4 字节边界对齐,但在 64 位系统中与 8 字节对齐。