【问题标题】:What am I missing from this K&R sample?我从这个 K&R 样本中遗漏了什么?
【发布时间】:2011-03-22 20:09:33
【问题描述】:

以下是 K&R 的 C 编程语言(第 8.7 节)中代码示例的摘录:

typedef long Align;

union header {
    struct {
        union header *ptr;
        unsigned size;
    } s;
    Align x;
};

typedef union header Header;

这里是解释性摘录:

为了简化对齐,所有块都是 标头大小的倍数,以及 标题正确对齐。这是 通过包含 所需的标题结构和一个 最严格类型的实例, 这是我们任意弄长的。

因此,据我了解,这个想法是确保header 实例占用的字节数是sizeof(Align) 的倍数。这个目标对我来说很有意义。

但是考虑long是64位,int是32位,指针是64位的情况。在这种情况下,header.s 将是 64+32 = 96 位。因此,sizeof(header) 将是 96 位,而不是预期的 64 的倍数。

在这种情况下,我想有必要将Align 定义为其他东西(也许是double)。但我不太确定我是否完全理解什么会决定特定架构的选择。它只是“最大”的类型吗?如果long 是最大的类型怎么办?它不能按照我上面描述的方式工作吗?

谢谢

【问题讨论】:

    标签: c memory


    【解决方案1】:

    编译器将调整header 的大小,以便ptrsizex 都可以从header[] 数组中的元素中高效检索。在大多数架构上,x 的存在意味着sizeof(header) 将增加到sizeof(long) 的倍数。

    【讨论】:

      【解决方案2】:

      联合的大小至少与其最大成员的大小一样大。因此,即使 sizeof(s) 是 12 并且 sizeof(long) 是 8,如您的示例中那样,您仍然会有 sizeof(header) >= 12。同样,联合的对齐是其任何成员的最大对齐,因此long 成员的存在确保header 的对齐至少是long 的对齐,在这种情况下为8 .

      如果你编译这段代码,你几乎肯定会发现sizeof(header)是16,而不是12。原因是如果你有一个header对象的数组,编译器需要确保任何一个对齐成员是正确的,并且数组连续存储在内存中。为了实现这一点,大小需要是对齐的倍数。编译器通过在 header 对象的末尾添加额外的 4 字节填充来实现这一点。

      【讨论】:

        【解决方案3】:

        我已经很久没有使用结构体了。但是,我确实使用了它们。主要原则是按对齐大小降序排列成员。

        在编写本文时,long 将与最大的寻址边界对齐。我相信情况仍然如此,但我很长时间没有关注硬件问题。一些硬件被设计成不能有效地寻址 long,除非它们的地址与 long 的大小对齐。

        我相信其意图是标题将在下一个有效寻址边界上对齐很长一段时间。这取决于硬件。如果 long 可在 32 位边界上有效寻址,则结构将适当对齐。如果 long 只能在 64 位边界上有效寻址,则结构将消耗 128 位以进行适当对齐。

        【讨论】:

        • 成员永远不会重新排列或重新排序——这是 C 标准所禁止的。相反,编译器可以(并且经常这样做)在成员之间和最后一个成员之后插入填充字节,以使对齐更有利。
        • @Adam Rosefield:因此,当程序员创建结构时,他们会以递减的对齐大小排列字段。我无意暗示编译器会这样做。 byte, long, byte, long 将是空间效率低下。 long, long, byte, byte 会更节省空间。如今,空间效率已不再是一个问题。
        【解决方案4】:

        自从我阅读 K&R 书以来已经很久(太久了),但我相信你是对的。您需要 typedef Align 到至少是相关平台上指针大小 + sizeof unsigned 的东西。

        我的猜测是,在编写(以及最后一次更新)本文时,32 位操作系统占据了至高无上的地位,而您的场景却逃脱了作者和编辑的关注。当然,他们更聪明/更聪明,我和我可能会偏离目标。

        【讨论】:

        • 更糟糕的是:当 K&R 上次更新时,(int) 仍然是 16 位。 (嗯,就是 ANSI K&R 之前的版本。我想知道这是否曾经被重新审视过。)
        【解决方案5】:

        正确地说,sizeof(Align) 必须大于或等于 sizeof(s)。但是今天的编译器真的不在乎,而是为您完成这项工作。

        #include <limits.h>
        #include <stdio.h>
        
        typedef long Align;
        
        typedef union header {
            struct {
                union header *ptr;  /* 64 bits */
                unsigned size;    /* 32 bits */
            } s;                    /* subtotal 96 */
            Align x;                /* 64 bits -> should be greater than 96 */
        } Header;                  /* total 128 bits */
        
        typedef struct hair {
            union header *ptr;  /* 64 bits */
            unsigned size;      /* 32 bits */
        } Hair;              /* subtotal 96 */
        
        int main(void)
        {
            Header u;
            Hair h;
        
            printf("char %ld bits, %ld bytes\n", sizeof(char)*CHAR_BIT,(sizeof(char)*CHAR_BIT)/8);
            printf("unsigned %ld bits, %ld bytes\n", sizeof(unsigned)*CHAR_BIT,(sizeof(unsigned)*CHAR_BIT)/8);
            printf("int %ld bits, %ld bytes\n", sizeof(int) * CHAR_BIT, (sizeof(int) * CHAR_BIT)/8);
            printf("long int %ld bits, %ld bytes\n", sizeof(long) * CHAR_BIT, (sizeof(long) * CHAR_BIT)/8);
            printf("long long int %ld bits, %ld bytes\n", sizeof(long long) * CHAR_BIT, (sizeof(long long) * CHAR_BIT)/8);
            printf("float %ld bits, %ld bytes\n", sizeof(float) * CHAR_BIT, (sizeof(float) * CHAR_BIT)/8);
            printf("double %ld bits, %ld bytes\n", sizeof(double) * CHAR_BIT, (sizeof(double) * CHAR_BIT)/8);
            printf("long double %ld bits, %ld bytes\n\n", sizeof(long double) * CHAR_BIT, (sizeof(long double) * CHAR_BIT)/8);
            printf("Header u %ld bits, %ld bytes\n", sizeof(u) * CHAR_BIT, (sizeof(u) * CHAR_BIT)/8);
            printf("Header *ptr %ld bits, %ld bytes\n", sizeof(u.s.ptr) * CHAR_BIT, (sizeof(u.s.ptr) * CHAR_BIT)/8);
            printf("Header size %ld bits, %ld bytes\n", sizeof(u.s.size) * CHAR_BIT, (sizeof(u.s.size) * CHAR_BIT)/8);
            printf("Header x %ld bits, %ld bytes\n\n", sizeof(u.x) * CHAR_BIT, (sizeof(u.x) * CHAR_BIT)/8);
            printf("Hair h %ld bits, %ld bytes\n", sizeof(h) * CHAR_BIT, (sizeof(h) * CHAR_BIT)/8);
            printf("Hair *ptr %ld bits, %ld bytes\n", sizeof(h.ptr) * CHAR_BIT, (sizeof(h.ptr) * CHAR_BIT)/8);
            printf("Hair size %ld bits, %ld bytes\n", sizeof(h.size) * CHAR_BIT, (sizeof(h.size) * CHAR_BIT)/8);
            return 0;
        }
        

        输出是:

        $ ./union-limits 
        char 8 bits, 1 bytes
        unsigned 32 bits, 4 bytes
        int 32 bits, 4 bytes
        long int 64 bits, 8 bytes
        long long int 64 bits, 8 bytes
        float 32 bits, 4 bytes
        double 64 bits, 8 bytes
        long double 128 bits, 16 bytes
        
        Header u 128 bits, 16 bytes
        Header *ptr 64 bits, 8 bytes
        Header size 32 bits, 4 bytes
        Header x 64 bits, 8 bytes
        
        Hair h 128 bits, 16 bytes
        Hair *ptr 64 bits, 8 bytes
        Hair size 32 bits, 4 bytes
        

        如您所见,Hair 与 Header 大小相同。但是对于不做这项工作的编译器,正确的做法是使用 long double 作为 Align。

        保重, 贝科。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-07
          • 1970-01-01
          • 2012-11-23
          • 1970-01-01
          相关资源
          最近更新 更多