【问题标题】:Second reinterpret_cast and strict aliasing第二次 reinterpret_cast 和严格别名
【发布时间】:2020-03-31 14:12:22
【问题描述】:

这是一个 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”的指针类型转换为“指向T2”的指针类型(其中T1 和T2 是对象类型,而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

【问题讨论】:

  • 哦,该死的。我没有阅读所有代码。这实际上没问题,因为f 实际上确实指向uint32_t。评论已撤消。

标签: c++ language-lawyer reinterpret-cast strict-aliasing


【解决方案1】:

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

“指向 cv1 void 的指针”类型的纯右值可以转换为“指向 cv2 T 的指针”类型的纯右值,其中 T 是对象类型,而 cv2 与 cv-qualification 相同或大于 cv-qualification ,简历1。如果原始指针值表示内存中一个字节的地址A,并且A不满足T的对齐要求,那么得到的指针值是未指定的。否则,如果原始指针值指向对象 a,并且存在与 a 指针可互转换的类型为 T(忽略 cv 限定)的对象 b,则结果是指向 b 的指针。否则,指针值不会因转换而改变。

(此文相关,因为本例中reinterpret_cast&lt;T*&gt;的结果为static_cast&lt;T *&gt;(static_cast&lt;void *&gt;(...)))

因此,如果满足对齐要求,则所有转换(uint32_t->float 和 float->uint32_t)都不会更改指针值。并且您访问对象作为它的类型。这里没有UB。

【讨论】:

    猜你喜欢
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 2019-02-09
    • 2017-10-23
    • 2012-11-08
    • 2019-01-29
    • 2016-11-21
    相关资源
    最近更新 更多