当询问C中常见的未定义行为时,灵魂比我提到的严格别名规则更加开明.
他们在说什么?
为了克服对齐问题,我需要记忆成一个临时的.临时的那种类型是什么?gcc抱怨以下reinterpret_cast将破坏严格的别名规则:
template <typename T>
T deserialize(char *ptr) {
static_assert(std::is_trivially_copyable<T>::value, "must be trivially copyable");
alignas(T) char raw[sizeof(T)];
memcpy(raw, ptr, sizeof(T));
return *reinterpret_cast<T *>(raw);
}
Run Code Online (Sandbox Code Playgroud)
(例如,当T为"长"时).
我不想定义T,因为我不想在覆盖之前构造T.
在一个联合中,不写一个成员然后读另一个计数作为未定义的行为?
template<typename T>
T deserialize(char *ptr) {
union {
char arr[sizeof(T)];
T obj;
} u;
memcpy(u.arr, ptr, sizeof(T)); // Write to u.arr
return u.obj; // Read from u.obj, even though arr is the active member.
}
Run Code Online (Sandbox Code Playgroud) 考虑以下代码:
void f(char * ptr)
{
auto int_ptr = reinterpret_cast<int*>(ptr); // <---- line of interest
// use int_ptr ...
}
void example_1()
{
int i = 10;
f(reinterpret_cast<char*>(&i));
}
void example_2()
{
alignas(alignof(int)) char storage[sizeof(int)];
new (&storage) int;
f(storage);
}
Run Code Online (Sandbox Code Playgroud)
感兴趣的行与example_1的调用:
Q1:在调用端,char指针使整数指针别名化.这是有效的.但是将它强制转换为int也是有效的吗?我们知道int在其生命周期内,但考虑该函数是在另一个转换单元中定义的(未启用链接时优化)并且上下文未知.然后所有编译器看到的是:int指针想要为char指针设置别名,这违反了严格的别名规则.允许这样吗?
Q2:考虑到不允许.我们在C++ 17中得到了std :: launder.它的一种指针优化屏障主要用于访问一个对象,该对象将新的位置放入其他类型的对象的存储中或者当涉及const成员时.我们可以用它来给编译器一个提示并防止未定义的行为吗?
从example_2调用的兴趣行:
Q3:这里应该要求std :: launder,因为这是Q2中描述的std :: launder用例,对吧?
auto int_ptr = std::launder(reinterpret_cast<int*>(ptr));
Run Code Online (Sandbox Code Playgroud)
但是再次考虑f
在另一个翻译单元中定义.编译器如何知道我们的新位置,这发生在呼叫端?编译器(仅查看函数f)如何区分example_1和example_2?或者仅仅是低调的,因为严格的别名规则只会排除所有内容(记住char*to int*不允许)并且编译器可以做他想做的事情?
跟进问题:
Q4:如果由于别名规则导致上述所有代码都出错,请考虑更改函数f
以获取void指针:
void f(void* ptr)
{
auto int_ptr = reinterpret_cast<int*>(ptr);
// use …
Run Code Online (Sandbox Code Playgroud)