C ++中错误访问的正确性

时间:2017-06-25 11:35:35

标签: c++ memory undefined-behavior memory-alignment

根据我的阅读,错位访问主要意味着两件事:

  1. 可能会获得性能损失
  2. 失去对齐访问权限
  3. 的加载和存储的原子性

    假设性能不是问题,我想从软件中获得的是正确性,错误访问有多糟糕?我的理解是x86 CPU将正确处理这些访问,但可能需要做额外的工作才能获取数据。

    让我问这个问题的原因是用-fsanitize=undefined编译我的代码。关于未对齐的商店/负载,我得到了很多错误。我不确定这是否是一个问题因为:

    1. 只在数据准备期间执行存储,这是一个单线程进程,所以我不关心原子性的损失
    2. 负载在多线程进程中执行,其中许多线程(四个或更多)访问数据,但数据未被任何修改(保存在const uint8_t*变量中)
    3. 访问未对齐的原因是const uint8_t*数组包含来自许多不同类型的字节(uint8_tuint16_tuint32_tuint64_tint64_t)。

      我确信没有加载超出分配的uint8_t数组的范围(例如,程序永远不会从指向分配的最后一个,两个或三个字节的地址加载uint64_t内存块),并确保我的访问都正确 - 只是未对齐。

      我读到的另一件事是,这样的加载可能违反了严格的别名规则,但代码编译时没有-Wstrict-aliasing -Werror的警告(我很久以前就已启用)。

      我应该填充uint8_t数组中的数据以确保访问对齐,还是可以安全地忽略警告?

2 个答案:

答案 0 :(得分:3)

有些平台不支持未对齐访问(您将遇到崩溃)。并且,有平台,支持未对齐访问,但有一些asm指令,需要对齐访问。例如,在ARM上,存在LDRD指令,其需要对齐的存储器地址。不幸的是,编译器可以自由使用该指令。但是,通常,有一个编译器扩展告诉编译器指针是未对齐的,所以它不会使用LDRD。

在支持UA的平台上,您提到了处罚。

我建议您使用memcpy 。它适用于所有平台,现在编译器非常好用于优化它(所以你不会获得memcpy调用,但是快速的mov指令)。

答案 1 :(得分:1)

主要问题不是性能或原子性,而是正确性。未对齐访问根据C和C ++标准调用未定义的行为,因此您不能依赖任何特定结果。它可能会起作用,也可能会崩溃。或者它可能先工作,并在以后停止工作。这是您获得的错误消息的本质。如果您知道它总是对您有用,您可以选择忽略错误,但由于您要求标记此类错误,使用相应的编译器开关,您应该努力避免它们,特别是如果您并不完全确定您的代码将永远保留在此平台上。此外,您怎么知道即使在同一平台上它也能为您服务?

从您编写的内容来看,数据似乎是由稍后读取的同一台机器编写的,只有线程不同。如果是这样,您应该尝试以正确对齐的方式写入数据,即在适当的地方使用填充。您可以通过将数据打包在正确定义的结构而不是非结构化缓冲区中来获得编译器的帮助。这也将为您提供更多的类型安全性。

否则你不得不担心不仅仅是对齐。例如,您还需要考虑字节序。在这种情况下,您可能正在编写一种可能最终位于不同计算机上的外部数据记录。您正在寻找一种机器中立的外部数据表示,您可以自己定义,或者更好地使用为RPC发明的几种标准表示之一,其优点是您可以找到用于读取和写入的库。