【问题标题】:Is this the correct explanation for this output of this program?这是对该程序的此输出的正确解释吗?
【发布时间】:2013-06-20 16:12:22
【问题描述】:
#include<stdio.h>

int main()
{
    struct test
    {
        char c;
        int y;
        float r;
        double d;
    } t1; 
    printf("%d\n",sizeof(t1));
    return 0;
}   

输出: 24 在我的 gcc 4.3.2 Ubuntu 12.04

输出:20 在 Ideone Link for Running Code

我的解释: 我认为 24​​ 是正确的。如果我错了,请纠正我?

  1. 第一个块将被字符“c”占用。 1 个字节为自己,后跟 3 个字节的填充。
  2. 第二个块将被整数'y'占用。 (在这种情况下不需要填充,因为整数是 4 字节。所以它将完全填充块)。
  3. 第三块将被浮动'r'占用。 (不需要填充,因为 float 也是 4 个字节)。
  4. 现在将填充下一个 4 字节的块,因为 next 是双倍的。并且存储地址必须是该数据类型大小的倍数。
  5. 接下来的 2 个 4 字节的块将被双变量占用。

因此Size = 1(char) + 3(Padded in Char case) + 4(int) + 4(float) + 4(padded for Double) + 8(double) = 24

为了清楚起见:

由于块是在 4 字节集合中获取的。所以每个地址都是 4 的倍数。 对于 double ,下一个地址必须是 1012。但它不是 8 的倍数。所以填充它!从 1016 开始 我的问题 - 24 是正确的,我的解释是正确的还是我的解释是错误的,请解释一下?

【问题讨论】:

  • 这里可能 20 和 24 都不正确。如果您看到一只黑猫,然后又看到一只白猫,那么无论出于何种原因,都没有理由认为黑色是猫的“正确”颜色。
  • 但 24 是我在运行代码之前对这个问题的回答。我认为 24​​ 也可以从逻辑上解释它!
  • 对齐是实现定义的。有些编译器可能会为双精度添加填充,有些可能不会,也不是“正确的”。至少这是我的理解。
  • 有些机器允许double被打包在一个4字节的边界上;其他要求 double 在 8 字节边界上打包。您所描述的对于必须在 8 字节边界上打包 double 的系统是正确的; ideone 给出的对于double 必须打包在 4 字节边界上的系统是正确的。因此,对于任何给定的系统,两者都可能是正确的。通常,只有其中一个是正确的。尽管同一台机器上的两个不同编译器可能具有不同的结构打包规则,但在实践中这不太可能成为问题——请参阅 ABI(应用程序二进制接口)。
  • %d 格式适用于int;对于size_t,使用%zu,或者,如果您的实现不支持它(它是C99 中的“新”),则转换为int: printf("%d\n", (int)sizeof t1); . And a very minor quibble: int main()` 应该是 int main(void)

标签: c structure


【解决方案1】:

您的解释是正确的,因为特定编译器会为该结构输出具有特定布局的二进制文件。如果您正确预测了输出,那么您的预测显然是正确的对于您在此特定时间的特定设置

但是结构体的大小在C 中并不是严格定义的,无论如何。在这个主题中唯一定义的是结构体成员的顺序和第一个成员的偏移量。填充,因此,结构的总大小是未定义的,它由编译器决定。显然,编译器填充结构的主要原因是通过根据成员的各个类型正确对齐成员来提高性能(正如您正确指出的那样)。但是(这在这里很重要)编译器会根据目标架构选择最有效的填充。例如,如果一个架构有一个 128 位的 int,您可以想象您的结构体的大小会有多大的不同。

所以,如果涉及到 C 语言的定义方面(或您描述之外的任何设置)您的预测将毫无根据。它自然会匹配 一些 设置,但只是偶然。

【讨论】:

  • 那么,“变量地址”是否真的应该以该变量大小的倍数开头。因为这是唯一影响该答案的因素?
  • @Shashank_Jain 任何事情都可能影响答案(包括用于以自定义方式打包结构的编译器指令)。但我相信,在大多数情况下,编译器试图将每个变量放在一个逻辑地址,该地址是其大小的倍数(如你所说)。但是,请记住,这在语言中是未定义的。记住(尤其是对于 C 和 C++ 等语言)非常重要的是,任何基于未定义前提的结论都应该被认为是错误的。
  • C++11 确实更精确地定义了对齐方式,但仍然对 sizeof 结构类型做出绝对零保证(除非它必须是正数)。
  • @aschepler 很高兴知道。我在 C++ 方面有些落后。
【解决方案2】:

填充用于使用指令优化。 C 语言没有指定任何关于填充的内容。唯一的问题是,如果 loadq(8 字节日期)不是从 8 字节地址边界开始,就会发生 H/W 陷阱。所以,我当然可以为这个示例数据结构编写一个没有任何填充的编译器。唯一的问题是我需要使用 loadb / storb 指令而不是 loadl / storl / loadq / storq 指令,这看起来很愚蠢。如果 Ideone 使用 loadl / storl 而不是 loadq / storq,20 字节大小是可以的,float 和 double 之间的填充是不必要的。

顺便说一句,我有在结构中按顺序声明 8 字节、4 字节、2 字节、1 字节数据的做法,并且可能在所有可能的地方......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多