为什么sizeof()编译器返回的值依赖?

时间:2013-12-03 14:25:25

标签: c++ c sizeof

struct A
{
    char c;
    double d;
} a;
  • 在mingw32-gcc.exe中:sizeof a = 16
  • 在gcc 4.6.3(ubuntu)中:sizeof a = 12

为什么他们不同?我认为应该是16,gcc4.6.3会做一些优化吗?

2 个答案:

答案 0 :(得分:11)

如果需要,编译器可以为目标体系结构执行数据结构对齐。它可能纯粹是为了提高应用程序的运行时性能,或者在某些情况下是处理器所要求的(即如果数据未对齐,程序将无法工作)。

例如,大多数(但不是全部)SSE2指令要求数据在16字节边界上对齐。简而言之,计算机内存中的所有内容都有一个地址。让我们说我们有一个简单的双打数组,如下所示:

double data[256];

为了使用需要16字节对齐的SSE2指令,必须确保&data[0]的地址是16的倍数。

对齐要求因架构而异。在x86_64上,建议所有大于16字节的结构在16字节边界上对齐。通常,为获得最佳性能,请按如下方式对齐数据:

  • 在任意地址对齐8位数据
  • 将16位数据对齐以包含在对齐的四字节字中
  • 对齐32位数据,使其基址为四的倍数
  • 对齐64位数据,使其基址为8的倍数
  • 对齐80位数据,使其基址为16的倍数
  • 对齐128位数据,使其基址为16的倍数

有趣的是,大多数x86_64 CPU都可以使用对齐和非对齐数据。但是,如果数据未正确对齐,则CPU执行代码的速度会慢得多。

当编译器考虑到这一点时,它可能会隐式地对齐结构的成员并且会影响其大小。例如,我们假设我们有这样的结构:

struct A {
    char a;
    int b;
};

假设x86_64,int的大小为32位或4个字节。因此,建议始终将b的地址设为4的倍数。但由于a字段大小仅为1个字节,因此无法实现。因此,编译器会隐式地在ab之间添加3个字节的填充:

struct A {
    char a;
    char __pad0[3]; /* This would be added by compiler,
                       without any field names - __pad0 is for
                       demonstration purposes */
    int b;
};

编译器如何做到这一点不仅取决于编译器和体系结构,还取决于传递给编译器的编译器设置(标志)。使用特殊语言结构也会影响此行为。例如,可以要求编译器不执行任何具有packed属性的填充,如下所示:

struct A {
    char a;
    int b;
} __attribute__((packed));

在您的情况下,mingw32-gcc.exe只在cd之间添加了7个字节,以便在8字节边界上对齐d。而Ubuntu上的gcc 4.6.3仅在4字节边界上添加了3对齐d

除非您正在执行某些优化,尝试使用特殊的扩展指令集,或者对数据结构有特定要求,否则我建议您不依赖于特定的编译器行为,并且始终假设您的结构不仅可能得到填充,它可能会在架构,编译器和/或不同的编译器版本之间进行不同的填充。否则,您需要使用编译器属性和设置半手动确保数据对齐和结构大小,并确保它在所有使用单元测试或甚至static assertions定位的编译器和平台上都有效。< / p>

有关详细信息,请查看:

希望它有所帮助。祝你好运!

如何最小化填充:

让所有结构成员正确对齐并同时保持结构大小合理始终是件好事。考虑这两个结构变体,重新安排成员(从现在开始假设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;
};

两个结构都应该保留相同的数据,但是当 sizeof(struct A)为12个字节时, sizeof(struct B)将是8个虽然out-out成员顺序消除了隐式填充:

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成员可能会因成员数增加而容易出错。为了减少错误 - 在开头放最长,在开头放最短:

struct B // no implicit padding
{
    int d;
    short b;
    char a;
    char c;
};

stuct结尾的隐式填充:

根据您使用的编译器,设置,平台等,您可能会注意到编译器不仅在结构成员之前添加填充,而且在结尾处(即在最后一个成员之后)添加填充。结构如下:

struct abcd
{
    long long a;
    char b;
};

可能占用12或16个字节(最差的编译器将允许它为9个字节)。这种填充可能很容易被忽视,但如果你的结构是阵列的话,这个非常重要。它将确保后续数组单元格/元素中的 成员也能正确对齐。

最终和随意的想法:

如果 - 在使用结构时 - 你会遵循这些建议,它永远不会伤害(并且实际上可能会保存):

  • 不要依赖编译器将结构成员与适当的填充交错。
  • 确保您的结构(如果外部数组)与其最长成员所需的边界对齐。
  • 确保排列结构成员,以便最长放置第一个,最后一个成员最短。
  • 确保明确填充结构(如果需要),以便在创建结构数组时,每个结构成员都具有正确的对齐方式。
  • 确保结构的数组也正确对齐,尽管结构可能需要8字节对齐,但编译器可能会将数组对齐为4字节边界。

答案 1 :(得分:7)

sizeofstructs返回的值不是任何C标准强制要求的。这取决于编译器和机器架构。

例如,在4字节边界上对齐数据成员可能是最佳的:在这种情况下,char c的有效打包大小将为4个字节。