什么是数据对齐?在 C 中对指针进行类型转换时,为什么以及何时应该担心?

新手上路,请多包涵

我找不到一个像样的文档来解释对齐系统的工作原理以及为什么某些类型比其他类型更严格对齐。

原文由 Dogus Ural 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 427
2 个回答

我会试着简短地解释一下。

什么是数据对齐?

您计算机中的体系结构由处理器和内存组成。内存是按单元格组织的,因此:

  0x00 |   data  |
 0x01 |   ...   |
 0x02 |   ...   |

每个存储单元都有一个指定的大小,它可以存储的位数。这取决于架构。

当您在 C/C++ 程序中定义变量时,您的程序会占用一个 或多个 不同的单元格。

例如

int variable = 12;

假设每个单元格包含 32 位,并且 int 类型大小为 32 位,那么在您的内存中某处:

 variable: | 0 0 0 c |  // c is hexadecimal of 12.

当您的 CPU 必须对该变量进行操作时,它需要将其放入其寄存器中。 CPU 可以从内存中获取“ _1 个时钟_”的少量位,该大小通常称为 WORD 。这个维度也依赖于架构。

现在假设您有一个变量,由于某些偏移,该变量存储在两个单元格中。

例如,我有两个不同的数据要存储(我将使用“ _字符串表示来更清楚_”):

 data1: "ab"
data2: "cdef"

所以内存将以这种方式组成(2个不同的单元格):

 |a b c d|     |e f 0 0|

data1 只占据了一半的cell,所以 data2 占据了剩余的部分和第二个cell的一部分。

现在假设您的 CPU 想要读取 data2 。 CPU 需要 2 个时钟 才能访问数据,因为在一个时钟内它读取第一个单元,而在另一个时钟内它读取第二个单元中的剩余部分。

如果我们按照这个内存示例 对齐 data2 ,我们可以在第二个单元格中引入一种 填充 和移位 data2

 |a b 0 0|     |c d e f|
     ---
   padding

这样,CPU 将只损失“ _1 个时钟_”以访问 data2

对齐系统的作用

根据架构,对齐系统只是引入了 _填充_,以便将数据与系统的内存对齐。

我为什么要关心对齐?

我不会深入回答这个问题。但是,从广义上讲,内存对齐来自上下文的要求。

在上面的示例中,使用填充(因此数据与内存对齐)可以节省 CPU 周期以检索数据。由于内存访问次数较少,这可能会对程序的执行性能产生影响。

然而,除了上面的例子(只是为了解释),还有很多其他的场景,内存对齐是有用的,甚至是需要的。

例如,某些架构可能对如何访问内存有严格的要求。在这种情况下,填充有助于分配满足平台约束的内存。

原文由 BiagioF 发布,翻译遵循 CC BY-SA 4.0 许可协议

这是“实现定义的”,即对齐要求不是语言规范的一部分。

不同的CPU对对齐有不同的要求。有些无法解决不均匀地址上的 16 位值,有些则可以。除非与可被其大小整除的地址对齐,否则有些无法寻址浮点值,有些则可以。等等。有些会比正确对齐的数据对象访问未对齐的数据对象更慢,其他人会因未对齐的访问而绊倒。

这就是为什么语言标准没有详细说明哪种类型需要以哪种方式对齐(因为它不能),而是将其留给“实现”——在这种情况下是编译器后端。

如果您对指针进行类型转换,您可能会强制代码在无法寻址的地址处寻址给定对象。您需要确保“旧”类型的对齐要求 至少 与“新”类型的对齐要求一样严格。

在 C++ (C++11 以上)中,您可以使用 alignof 运算符来告诉您给定类型的对齐要求。您还可以使用 alignas 运算符对给定类型或对象强制执行更严格的对齐。

在 C 中(C11 向上),您会得到 _Alignof_Alignas 运算符,它们 <stdalign.h> 包装到 alignof / alignas 方便宏。 (谢谢,Lundin——C11 不是我的强项。)

原文由 DevSolar 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题