【问题标题】:Data layouts used by C compilers (the alignment concept)C 编译器使用的数据布局(对齐概念)
【发布时间】:2026-01-28 21:10:02
【问题描述】:

以下是红龙书的节选。

示例 7.3。图 7.9 是 C 编译器对两台机器使用的数据布局的简化图,我们称之为 Machine 1Machine 2

Machine 1Machine 1 的内存被组织成字节,每个字节由 8 位组成。尽管每个字节都有一个地址,但指令集倾向于将short 整数定位在地址为偶数的字节上,并且将整数定位在可被4 整除的地址上。编译器将短整数放置在偶数地址,即使它必须在进程中跳过一个字节作为填充。因此,由32 位组成的四个字节可以分配给后跟一个短整数的字符。

Machine 2每个字由64 位组成,24 位允许用于字的地址。 64 一个字中的各个位有可能,因此需要6 额外的位来区分它们。按照设计,指向Machine 2 上的字符的指针使用30 位——24 用于查找单词,6 用于查找单词中字符的位置。 指令的强字定位Machine 2 的集合导致编译器一次分配一个完整的字,即使更少的位足以表示该类型的所有可能值;例如,只需要8 位来表示一个字符。因此,在对齐情况下,图 7.9 显示了每种类型的64 位。在每个字中,每种基本类型的位都在指定的位置。由128 位组成的两个字将分配给后跟一个短整数的字符,该字符仅使用第一个字中的8 位,而短整数仅使用第二个字中的24 位单词。 □

我发现了对齐hereherehere 的概念。我可以从中了解到如下: 在字可寻址 CPU(大小超过一个字节)中,在数据对象中引入了某些填充,以便 CPU 可以以最少的数量有效地从内存中检索数据。内存周期。

现在这里的Machine 1 实际上是一个字节地址。并且Machine 1 规范中的条件可能比字长为4 字节的简单字可寻址机器更困难。在这样的64 位机中,我们需要确保我们的数据项只是字对齐,没有更多困难。但是如何在像Machine 1(如上表中给出的)这样的系统中找到对齐方式,因为字对齐的简单概念不起作用,因为它是字节可寻址的并且具有更难的规范。

此外,我发现在double 的行中,类型的大小大于对齐字段中给出的大小,这很奇怪。不应该alignment(in bits) ≥ size (in bits) 吗?因为对齐是指实际为数据对象分配的内存(?)。

"每个字由64 位组成,而24 位允许用于字的地址。字中的各个位有64 的可能性,所以6 附加位需要区分它们。根据设计,指向Machine 2 上的字符的指针使用30 位——24 用于查找单词,6 用于查找字符在单词中的位置。” - 此外,这个关于指针概念的陈述应该如何可视化,基于对齐(2^6 = 64,很好,但是这 6 位与对齐概念有什么关系)

【问题讨论】:

    标签: c compiler-construction local memory-alignment runtime-environment


    【解决方案1】:

    首先,机器 1 一点也不特别——它与 x86-32 或 32 位 ARM 完全一样。

    此外,我发现在行中双倍类型的大小大于对齐字段中给出的大小,这很奇怪。不应该对齐(以位为单位)≥大小(以位为单位)吗?因为对齐是指实际为数据对象分配的内存(?)。

    不,这不是真的。对齐意味着对象中最低可寻址字节的地址必须能被给定的字节数整除。

    此外,对于 C,在 数组sizeof (ElementType) 也需要大于或等于对齐每个成员 sizeof (ElementType) 可以被对齐整除,因此脚注a。因此在后一种计算机上:

     struct { char a, b; }
    

    可能有 16 的大小,因为字符位于不同的可寻址单词中,而

    struct { char a[2];  }
    

    可以压缩成 8 个字节。


    这个关于指针概念的陈述应该如何可视化,基于对齐方式(2^6 = 64,很好,但是这 6 位与对齐概念有什么关系)

    至于字符指针,6 位是假的。 8-byte字中的8个字节中选择一个需要3位,所以这是书中的错误。一个普通字节只会选择一个 24 位的字,而一个字符(一个字节)指针会选择一个 24 位的字,而字内的一个 8 位字节会选择 3 位。

    【讨论】:

    • 这里另一个有趣的事实是,C 允许指向不同类型的指针具有不同的格式,“Machine 2”实现利用了这一事实。这一切都有效,因为不能保证将指针转换为整数类型会产生以字节为单位的值;换句话说,您不能通过简单地将指针转换为intptr_t 并使用类型的对齐检查余数来便携式测试指针是否“正确对齐”。 (如果您首先转换为void*,该测试应该可以工作,但我认为即使这样也不能保证。)