【发布时间】:2012-10-28 17:36:19
【问题描述】:
举个例子
class A
{
public:
int a;
char b;
int c;
};
我看到的每个编译器(对于 x86、32 或 64 位)都为类 A 分配 12 个字节,而不是 9 个。因此它们将 b 与您可以说的整数边界或总线边界对齐。我的问题是这是否符合 C++ 标准,是否有任何编译器不这样做。
【问题讨论】:
举个例子
class A
{
public:
int a;
char b;
int c;
};
我看到的每个编译器(对于 x86、32 或 64 位)都为类 A 分配 12 个字节,而不是 9 个。因此它们将 b 与您可以说的整数边界或总线边界对齐。我的问题是这是否符合 C++ 标准,是否有任何编译器不这样做。
【问题讨论】:
在您的情况下,“b”始终正确对齐。 padded 以在 32 位边界中对齐 c。尽管有一些特定实现的空间,但大多数编译器都遵循将 2 字节和 4 字节变量与 2 字节和 4 字节边界对齐的规则。
在 32 位系统中,double 和 long int(8 字节)也对齐到 4 字节边界,但在 64 位系统中对齐到 8 字节。
【讨论】:
long double 的各种对齐方式。
C++ 标准规定:
int 的宽度为 4 个字节,则它需要 1、2 或 4 个字节的对齐,具体取决于实现)。public等访问说明符分隔)全部按照它们声明的顺序分配所以不,标准并没有明确说明该类的大小应为 12 字节。
但它确实说b应该在a之后分配,而c应该在b之后分配。
在int 为 4 字节宽且需要 4 字节对齐的平台上,这会留下 12 字节作为最小有效大小:
a 占用前 4 个字节b 占用一个字节c 需要 4 个字节,但必须在 4 个字节的边界上分配。 b 结束了一个字节过去这样的边界,因此通过插入 3 个字节的填充找到放置 c 的下一个有效位置。所以类的总大小最终是成员的大小 (4 + 1 + 4 = 9) 加上三个字节的填充,总共 12 个。
这里还有一条无效的规则,但是如果您按照a, c, b 的顺序定义了成员,这将很重要。
包含类 (A) 从最严格对齐的成员对象继承对齐要求。也就是说,因为它包含一个int,它具有与int 相同的对齐要求。并且因为对象的总大小必须是其对齐要求的倍数,包含顺序为a, b, c 的成员的类仍然需要 12 个字节的存储空间。它只是将 3 个字节的填充移到类的末尾,而不是在 b 和 c 之间。
但是,在某些其他情况下,按大小降序重新排序成员有时会减小类的大小。
假设我们有一个这样的类:
class B {
char a;
double b;
int c;
};
这将需要 24 个字节的存储空间(a 需要 1 个字节,b 需要 8 个字节,c 需要 4 个字节,但是为了确保b 以 8 字节边界结束,我们需要在a 和b 之间有 7 个字节的填充,并且为了确保整个类的大小是 8 的倍数,我们需要在 @ 之后再增加 4 个字节987654347@.
但是根据大小重新排序成员,如下所示:
class B {
double b;
int c;
char a;
};
生成一个只需要 16 个字节的类:
成员对象本身也是相同的 1 + 4 + 8 字节,但现在 c 已经在 4 字节边界上对齐(因为它位于以 8 字节边界结束的 b 之后),并且a 从来不需要任何对齐,所以我们唯一需要的对齐是确保B 的大小是8 的倍数。成员占用13 个字节,所以我们可以添加3 个字节的填充,类结束最多 16 个字节,比第一个版本小 33%。
【讨论】:
可以在编译时使用 pragma 指令控制结构打包。 See #Pragma Pack
例如,以下不对齐成员,而是将它们彼此相邻放置。
#pragma pack(push, 1)
struct A4
{
char a;
double b;
char c;
};
#pragma pack(pop)
来自 here 的示例。
GCC 也支持编译指示包。该指令不是某些标准的一部分,但很多编译器都支持它。
但是,不应该有这样做的理由。编译器将它们对齐以加快对成员的访问,并且应该没有理由改变它。
【讨论】:
pack 指令不是任何标准 AFAIK 的一部分。
#pragma anything 根据定义不属于任何标准。
是的,标准中到处都提到了对齐,主要是第 3.11 节(对齐)。它依赖于平台,因此任何依赖于对象实际大小的程序本质上都是不可移植的。
【讨论】:
标准允许编译器在必要时填充更多字节。它基本上取决于主机的架构而不是编译器。
【讨论】: