关于内存对齐的附加问题

Additional questions on memory alignment

以前有一些关于内存对齐的很好的答案,但我觉得没有完全回答一些问题。

例如:

What is aligned memory allocation?

我有一个示例程序:

#include <iostream>
#include <vector>
#include <cstring>

int32_t cast_1(int offset) {
  std::vector<char> x = {1,2,3,4,5};
  return reinterpret_cast<int32_t*>(x.data()+offset)[0];
}

int32_t cast_2(int offset) {
  std::vector<char> x = {1,2,3,4,5};
  int32_t y;
  std::memcpy(reinterpret_cast<char*>(&y), x.data() + offset, 4);
  return y;
}

int main() {
  std::cout << cast_1(1) << std::endl;
  std::cout << cast_2(1) << std::endl;
  return 0;
}

cast_1 函数输出 ubsan 对齐错误(正如预期的那样),但 cast_2 没有。但是,cast_2 对我来说可读性要差得多(需要 3 行)。 cast_1 尽管它是 UB,但其意图看起来非常清楚。

问题:

1) 为什么 cast_1 UB 的意图非常明确?我了解对齐可能存在性能问题。

2) cast_2 是修复 cast_1 UB 的正确方法吗?

1) Why is cast_1 UB?

因为语言规则是这么说的。实际上有多个规则。

  1. 您访问对象的偏移量不符合 int32_t 的对齐要求(对齐要求为 1 的系统除外)。不能创建不符合类型对齐要求的对象。

  2. char 指针不能被 int32_t 指针别名。

2) Is cast_2 a correct approach to fixing the UB of cast_1?

cast_2 具有明确定义的行为。那个函数里的reinterpret_cast是多余的,用魔法常数不好(用sizeof)。

关于第一个问题,编译器为您处理这个问题是微不足道的,是的。它所要做的就是悲观化程序中的所有其他非字符加载

对齐规则的编写非常精确,因此编译器可以生成在许多平台上都表现良好的代码,在这些平台上,对齐内存访问是一种快速的本机操作,而未对齐访问相当于您的 memcpy。除了可以证明对齐的地方,编译器必须以缓慢而安全的方式处理每个加载。