第二次reinterpret_cast和严格混叠

时间:2019-12-06 15:03:04

标签: c++ language-lawyer reinterpret-cast 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

  

注意:将类型为“指向T1的指针”的prvalue转换为“指向“ T2的指针”的类型(其中T1T2是对象类型,其中T2的对齐要求不严格于T1的对齐要求,并返回其原始类型将产生原始指针值。

我们使用类型为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

1 个答案:

答案 0 :(得分:1)

以下是相应的规范性文本expr.static_cast/13

  

“指针指向cv1无效”类型的prvalue可以转换为“指针指向cv2 T的类型”的prvalue,其中T是对象类型,并且cv2是与cv-qualification相同或大于cv-qualification的对象。 ,cv1。如果原始指针值表示内存中一个字节的地址A,而A不满足T的对齐要求,则未指定结果指针值。否则,如果原始指针值指向对象a,并且存在类型T(忽略cv限定)的对象b,该对象可与a进行指针互换,则结果是指向b的指针。否则,转换后指针值不变

(此文本与之相关,因为在这种情况下,reinterpret_cast<T*>的结果为static_cast<T *>(static_cast<void *>(...))

因此,如果满足对齐要求,则所有转换(uint32_t-> floatfloat-> uint32_t)都不会更改指针值。然后您访问该对象作为其类型。这里没有UB。