C++ 为什么sizeof()编译器返回的值是依赖的? 在mingw32-gcc.exe中:sizeof a=16 在GCC4.6.3(ubuntu)中:sizeof a=12
为什么它们不同?我认为应该是16,gcc4.6.3是否进行了一些优化?任何C标准都不强制要求C++ 为什么sizeof()编译器返回的值是依赖的? 在mingw32-gcc.exe中:sizeof a=16 在GCC4.6.3(ubuntu)中:sizeof a=12,c++,c,sizeof,C++,C,Sizeof,为什么它们不同?我认为应该是16,gcc4.6.3是否进行了一些优化?任何C标准都不强制要求结构的sizeof返回的值。这取决于编译器和机器体系结构 例如,在4字节边界上对齐数据成员可能是最佳选择:在这种情况下,char c的有效压缩大小将为4字节。如果需要,编译器可能会对目标体系结构执行数据结构对齐。这样做可能纯粹是为了提高应用程序的运行时性能,或者在某些情况下是处理器所必需的(即,如果数据不对齐,程序将无法工作) 例如,大多数(但不是全部)指令要求数据在16字节边界上对齐。简单地说,计算机内
结构的sizeof
返回的值。这取决于编译器和机器体系结构
例如,在4字节边界上对齐数据成员可能是最佳选择:在这种情况下,char c
的有效压缩大小将为4字节。如果需要,编译器可能会对目标体系结构执行数据结构对齐。这样做可能纯粹是为了提高应用程序的运行时性能,或者在某些情况下是处理器所必需的(即,如果数据不对齐,程序将无法工作)
例如,大多数(但不是全部)指令要求数据在16字节边界上对齐。简单地说,计算机内存中的所有东西都有一个地址。假设我们有一个简单的双精度数组,如下所示:
struct A
{
char c;
double d;
} a;
double data[256];
struct A {
char a;
char __pad0[3]; /* This would be added by compiler,
without any field names - __pad0 is for
demonstration purposes */
int b;
};
为了使用需要16字节对齐的SSE2指令,必须确保&data[0]
的地址是16的倍数
不同体系结构的对齐要求不同。在x86_64上,建议所有大于16字节的结构在16字节边界上对齐。通常,为了获得最佳性能,请按如下方式对齐数据:
- 在任意地址对齐8位数据
- 对齐要包含在对齐的四字节字中的16位数据
- 对齐32位数据,使其基址为4的倍数
- 对齐64位数据,使其基址为8的倍数
- 对齐80位数据,使其基址为16的倍数
- 对齐128位数据,使其基址为16的倍数
有趣的是,大多数x86_64 CPU都可以处理对齐和非对齐数据。但是,如果数据没有正确对齐,CPU执行代码的速度会显著降低
当编译器考虑到这一点时,它可能会隐式地对齐结构的成员,这会影响结构的大小。例如,假设我们有这样一个结构:
struct A
{
char c;
double d;
} a;
double data[256];
struct A {
char a;
char __pad0[3]; /* This would be added by compiler,
without any field names - __pad0 is for
demonstration purposes */
int b;
};
假设x86_64,int
的大小为32位或4字节。因此,建议始终将b
的地址设置为4的倍数。但是因为字段大小只有1字节,所以这是不可能的。因此,编译器将在a
和b
之间隐式添加3个字节的填充:
struct A {
char a;
int b;
};
编译器的工作方式不仅取决于编译器和体系结构,还取决于传递给编译器的编译器设置(标志)。使用特殊的语言结构也会影响此行为。例如,可以要求编译器不使用packed
属性执行任何填充,如下所示:
struct A
{
char c;
double d;
} a;
double data[256];
struct A {
char a;
char __pad0[3]; /* This would be added by compiler,
without any field names - __pad0 is for
demonstration purposes */
int b;
};
在您的例子中,mingw32 gcc.exe
只是在c
和d
之间添加了7个字节,以便在8字节边界上对齐d
。而Ubuntu上的GCC4.6.3只增加了3个字节来对齐4字节边界上的d
除非您正在执行一些优化,尝试使用特殊的扩展指令集,或者对数据结构有特定的要求,否则我建议您不要依赖于特定的编译器行为,并且始终假设不仅您的结构可能会被填充,而且架构之间的填充也可能不同,编译器和/或不同的编译器版本。否则,您需要使用编译器属性和设置半手动地确保数据对齐和结构大小,并使用单元测试甚至可能使用单元测试确保所有编译器和平台都能正常工作
有关更多信息,请查看:
- 关于维基百科的文章
希望能有帮助。祝你好运
如何最小化填充:
让所有结构成员正确对齐,同时保持结构大小合理,这总是一件好事。考虑这2个结构变量,其中成员重新排列(从现在开始假设字符长度,长度,长度,长度分别为1, 2, 4,4, 8):
这两个结构都应该保留相同的数据,但sizeof(结构A)将为12字节,sizeof(结构B)将为8字节,这是由于良好的外部成员顺序消除了隐式填充:
struct A
{
char a;
short b;
char c;
int d;
};
struct B
{
char a;
char c;
short b;
int d;
};
随着成员数的增加,重新排列结构成员可能容易出错。为了减少出错的可能性,请将最长的放在开头,最短的放在结尾:
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;
};
语句末尾的隐式填充:
根据您使用的编译器、设置、平台等,您可能会注意到编译器不仅在结构成员之前添加填充,而且在最后(即在最后一个成员之后)添加填充。结构如下:
struct B // no implicit padding
{
int d;
short b;
char a;
char c;
};
可能占用12或16个字节(最差的编译器将允许9个字节)。这种填充可能很容易被忽略,但如果您的结构是数组元素,那么它是非常重要的。它将确保后续数组单元格/元素中的成员也正确对齐
最终和随机想法:
在使用structs时,如果您遵循以下建议,它将永远不会伤害您(实际上可能会拯救您):
- 不要依赖编译器以适当的填充来交错结构成员
- 确保结构(若在数组外部)与其最长成员所需的边界对齐
- 请确保对结构成员进行排列,使最长的成员放在第一位,最后一个成员最短
- 确保显式填充结构(如果需要),以便在创建结构数组时,每个结构成员都有正确的对齐方式
- 确保结构的数组也正确对齐,因为尽管结构可能需要8字节对齐,但编译器可能会以4字节bou对齐数组