关于内存对齐的附加问题
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?
因为语言规则是这么说的。实际上有多个规则。
您访问对象的偏移量不符合 int32_t
的对齐要求(对齐要求为 1 的系统除外)。不能创建不符合类型对齐要求的对象。
char
指针不能被 int32_t
指针别名。
2) Is cast_2
a correct approach to fixing the UB of cast_1
?
cast_2
具有明确定义的行为。那个函数里的reinterpret_cast
是多余的,用魔法常数不好(用sizeof
)。
关于第一个问题,编译器为您处理这个问题是微不足道的,是的。它所要做的就是悲观化程序中的所有其他非字符加载。
对齐规则的编写非常精确,因此编译器可以生成在许多平台上都表现良好的代码,在这些平台上,对齐内存访问是一种快速的本机操作,而未对齐访问相当于您的 memcpy。除了可以证明对齐的地方,编译器必须以缓慢而安全的方式处理每个加载。
以前有一些关于内存对齐的很好的答案,但我觉得没有完全回答一些问题。
例如:
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?
因为语言规则是这么说的。实际上有多个规则。
您访问对象的偏移量不符合
int32_t
的对齐要求(对齐要求为 1 的系统除外)。不能创建不符合类型对齐要求的对象。char
指针不能被int32_t
指针别名。
2) Is
cast_2
a correct approach to fixing the UB ofcast_1
?
cast_2
具有明确定义的行为。那个函数里的reinterpret_cast
是多余的,用魔法常数不好(用sizeof
)。
关于第一个问题,编译器为您处理这个问题是微不足道的,是的。它所要做的就是悲观化程序中的所有其他非字符加载。
对齐规则的编写非常精确,因此编译器可以生成在许多平台上都表现良好的代码,在这些平台上,对齐内存访问是一种快速的本机操作,而未对齐访问相当于您的 memcpy。除了可以证明对齐的地方,编译器必须以缓慢而安全的方式处理每个加载。