struct A
{
char c;
double d;
} a;
Run Code Online (Sandbox Code Playgroud)
sizeof a = 16sizeof a = 12为什么他们不同?我认为它应该是16,gcc4.6.3是否做了一些优化?
小智 11
如果需要,编译器可以为目标体系结构执行数据结构对齐.它可能纯粹是为了提高应用程序的运行时性能,或者在某些情况下是处理器所要求的(即如果数据未对齐,程序将无法工作).
例如,大多数(但不是全部)SSE2指令要求数据在16字节边界上对齐.简而言之,计算机内存中的所有内容都有一个地址.假设我们有一个简单的双精度数组,如下所示:
double data[256];
Run Code Online (Sandbox Code Playgroud)
为了使用需要16字节对齐的SSE2指令,必须确保地址&data[0]为16的倍数.
对齐要求因架构而异.在x86_64上,建议所有大于16字节的结构在16字节边界上对齐.通常,为获得最佳性能,请按如下方式对齐数据:
有趣的是,大多数x86_64 CPU都可以使用对齐和非对齐数据.但是,如果数据未正确对齐,CPU执行代码的速度会明显变慢.
当编译器考虑到这一点时,它可能会隐式地对齐结构的成员并且会影响其大小.例如,假设我们有这样的结构:
struct A {
char a;
int b;
};
Run Code Online (Sandbox Code Playgroud)
假设x86_64,大小int为32位或4字节.因此,建议始终使地址为4 b的倍数.但由于a字段大小只有1个字节,因此无法实现.因此,编译器会在其间添加3个字节的填充a并b隐式:
struct A {
char a;
char __pad0[3]; /* This would be added by compiler,
without any field names - __pad0 is for
demonstration purposes */
int b;
};
Run Code Online (Sandbox Code Playgroud)
编译器如何做到这一点不仅取决于编译器和体系结构,还取决于传递给编译器的编译器设置(标志).使用特殊语言结构也会影响此行为.例如,可以要求编译器不使用如下packed属性执行任何填充:
struct A {
char a;
int b;
} __attribute__((packed));
Run Code Online (Sandbox Code Playgroud)
在你的情况下,mingw32-gcc.exe简单地在8字节边界之间添加7个字节c并d对齐d.而Ubuntu上的gcc 4.6.3只添加了3个以对齐d4字节边界.
除非您正在执行某些优化,尝试使用特殊的扩展指令集,或者对数据结构有特定要求,否则我建议您不要依赖于特定的编译器行为,并且总是假设不仅您的结构可能被填充,它可能在架构,编译器和/或不同的编译器版本之间进行不同的填充.否则,您需要使用编译器属性和设置半手动确保数据对齐和结构大小,并确保它在所有使用单元测试或甚至静态断言的目标编译器和平台上都有效.
欲了解更多信息,请查看:
希望能帮助到你.祝好运!
如何最小化填充:
让所有结构成员正确对齐并同时保持结构大小合理始终是件好事.考虑这两个结构变体,重新安排成员(从现在开始假设sizeof char,short,int,long,long long分别为1,2,4,4,8):
struct A
{
char a;
short b;
char c;
int d;
};
struct B
{
char a;
char c;
short b;
int d;
};
Run Code Online (Sandbox Code Playgroud)
两个结构都应该保持相同的数据,但sizeof(结构A)将是12个字节,sizeof(结构B)将是8,因为良好的成员顺序消除了隐式填充:
struct A
{
char a;
char __pad0[1]; // implicit compiler padding
short b;
char c;
char __pad1[3]; // implicit compiler padding
int d;
};
struct B // no implicit padding
{
char a;
char c;
short b;
int d;
};
Run Code Online (Sandbox Code Playgroud)
随着成员数量的增加,重新排列结构成员可能容易出错.为了减少错误 - 在开头放最长,最后放最短:
struct B // no implicit padding
{
int d;
short b;
char a;
char c;
};
Run Code Online (Sandbox Code Playgroud)
结束时的隐式填充:
根据您使用的编译器,设置,平台等,您可能会注意到编译器不仅在结构成员之前添加填充,而且在结尾处(即在最后一个成员之后)添加填充.结构如下:
struct abcd
{
long long a;
char b;
};
Run Code Online (Sandbox Code Playgroud)
可能占用12或16个字节(最差的编译器将允许它为9个字节).这种填充可能很容易被忽视,但如果你的结构是阵列的话,这个非常重要.它将确保您后续数组单元格/元素中的成员也将正确对齐.
最后和随意的想法:
如果 - 在使用结构时 - 你会遵循这些建议,它永远不会伤害(并且可能实际上保存)你:
sizeoffor 返回的值structs不是任何C标准强制要求的.这取决于编译器和机器架构.
例如,在4字节边界上对齐数据成员可能是最佳的:在这种情况下,有效打包大小char c将是4个字节.