【问题标题】:Malloc and unaligned memoryMalloc 和未对齐的内存
【发布时间】:2013-06-27 04:05:06
【问题描述】:

malloc() 文档说返回的指针适合任何类型的变量对齐。在我的系统上,long double 的大小为 12,但是 malloc() 返回的一些指针在 8 字节边界上对齐,而不是 12。(ptr % 12 给出 4)。谁能指出我在这里错过了什么?提前致谢。

【问题讨论】:

  • 您从哪里了解到 malloc 返回对齐的内存?
  • 在手册页中。请参阅此处的返回值linux.die.net/man/3/malloc。还是我误解了什么?
  • 12 字节长的双精度数不需要 12 字节对齐。
  • 什么系统有一个 12 字节长的 double?
  • 对齐限制源于 CPU 总线逻辑设计。不深入技术细节,你只会看到 2 的幂。12 不是 2 的幂,不能是对齐大小。

标签: c malloc


【解决方案1】:

显然,long double 不需要对齐到 12 个字节。在像您这样的系统上,原始 C 类型的最大对齐是 double 的 8 个字节。不要混淆对齐和大小 - 虽然许多较小(尤其是原始)类型必须自然对齐 - 即与它们自己的大小对齐 - 这对于较大的数据类型(考虑碎片)是禁止的,并且不会使任何事情变得更容易硬件。

【讨论】:

  • @undefinedbehaviour: structs.
  • @MatteoItalia ...但结构的第一个成员必须适当对齐,否则会发生未定义的行为。这是没有意义的。您知道可以在 Intel 机器上打开总线错误检查吗?也许你应该在做出断言之前验证这一点......
  • @undefinedbehaviour long double 对于初学者。 struct 的成员当然必须适当对齐(不确定是只有第一个还是全部),但整个结构没有进一步的对齐要求。想象一下,如果struct IntHashSet { uint32_t used; uint32_t capacity; int32_t *table; int32_t smalltable[60 / 4]; } 需要 72 字节对齐。
  • @MatteoItalia 不遵守英特尔处理器上的对齐通常不会使您的程序崩溃,但它会使您的程序运行速度变慢
  • 另一个例子 - 在 Linux i686 上,long long 的大小为 8,对齐方式为 4。
【解决方案2】:

gcc for Linux 和x86 你有:

sizeof (long double) == 12

gcc for Linux 和x64 你有:

sizeof (long double) == 16

x64long double 的 ABI 对齐要求是 16-bytes。对于x86,没有任何类型需要超过8-bytes 的对齐。

尊重glibc malloc 返回与32-bit 系统上的8-bytes 对齐的内存对象,并在64-bit 系统上与16-bytes 对齐。

来自glibc文档:

malloc 为您提供的块保证是对齐的,以便它可以容纳任何类型的数据。在 GNU 系统上,地址在大多数系统上总是 8 的倍数,在 64 位系统上总是 16 的倍数。

【讨论】:

    【解决方案3】:

    在任何合理的 cpu 架构上,大小为 12 的对象的最大可能对齐要求是 4。对齐要求必须是 2 的幂,将类型的大小均分。实际上,这会导致非常差的对齐(对象跨越缓存行甚至页面!),这就是 x86_64 ABI 将 long double 的大小更改为 16: 的原因,以便可以对齐它而不跨越任何边界。

    【讨论】:

      【解决方案4】:

      malloc 将返回针对任何类型正确对齐的内存,因为通常它返回的指针被转换为任意类型的指针,并且这种转换通常(阅读:无限接近 100% 的时间)是禁止的同上。然而,对于任何体系结构和编译器,任何类型的最大对齐都是一个常数,通常它是所有原始类型的最大对齐。然而,对象的大小应该是其对齐方式的倍数。有时必须通过向对象内部的值添加填充来满足此要求。

      如果您的编译器足够现代,它将支持_Alignof,类似于sizeof,因此您可以使用小程序检查实际对齐情况。我认为在您的体系结构上,最大对齐是 4。如果 malloc 总是返回按 8 对齐的地址,那么它仍然符合要求。 malloc 满足类型的最大对齐倍数的对齐并不罕见。

      【讨论】:

        【解决方案5】:

        您错过了,虽然类型的大小必须是其对齐方式的倍数(受实际硬件限制),但它们不必相等。

        如果是long double,则转换为

        _Alignof (long double) == 4
        sizeof (long double) == 12
        

        使用 gcc/x86 和

        _Alignof (long double) == 16
        sizeof (long double) == 16
        

        使用 gcc/x64,long double 在这两种情况下都具有 80 位扩展精度。

        如果我们不受对齐方式的限制,我们很自然会得到

        sizeof (long double) == 10
        

        在存在对齐的情况下,我们要么必须使用 2 对齐(出于效率原因,我们不会这样做),要么向数组引入填充以使元素正确对齐。这违反了 C 语言语义,因为数组的大小是其元素大小和计数的乘积。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-22
          • 2017-06-27
          • 2010-11-07
          • 2015-03-22
          • 2018-09-01
          • 2015-05-19
          • 1970-01-01
          • 2013-10-21
          相关资源
          最近更新 更多