第二 reinterpret_cast 和严格的别名

Second reinterpret_cast and strict aliasing

这是 classical example 严格的别名违规:

std::uint32_t foo(float* f, std::uint32_t* i) {
    *i = 1;
    *f = 2;
    return *i;
}

int main() {
    std::uint32_t i = 3;
    foo(reinterpret_cast<float*>(&i), &i);
}

但是假设我们添加第二个 reinterpret_cast:

static_assert(alignof(float) == alignof(std::uint32_t));

std::uint32_t foo(float* f, std::uint32_t* i) {
    *i = 1;
    *reinterpret_cast<std::uint32_t*>(f) = 2;
    return *i;
}

int main() {
    std::uint32_t i = 3;
    std::uint32_t j = foo(reinterpret_cast<float*>(&i), &i);
    assert(j == 2);
}

此代码是否正确(未调用未定义的行为)?

标准 [expr.reinterpret.cast] reads:

Note: Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value.

我们使用std::uint32_t*类型的原始指针值来访问std::uint32_t类型的左值。

打开优化后,GCC 和 Clang 都能生成正确的汇编代码:

foo(float*, unsigned int*):
        mov     dword ptr [rsi], 1
        mov     dword ptr [rdi], 2
        mov     eax, dword ptr [rsi]
        ret

这里是相应的规范文本,expr.static_cast/13:

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If the original pointer value represents the address A of a byte in memory and A does not satisfy the alignment requirement of T, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.

(本文有关系,因为在这种情况下,reinterpret_cast<T*>的结果是static_cast<T *>(static_cast<void *>(...))

因此,如果满足对齐要求,那么 none 次转换(既不是 uint32_t->float 也不是 float->uint32_t)改变指针值。并且您将对象作为其类型进行访问。这里没有UB