使用reinterpret_cast签名混叠

Signedness aliasing using reinterpret_cast

本文关键字:cast reinterpret 使用      更新时间:2023-10-16

采取以下代码

#include <iostream>
void func() {
    int i = 2147483640;
    while (i < i + 1)
    {
        std::cerr << i << 'n';
        ++i;
    }
    return;
}
int main() {
    func(); 
}

此代码显然是错误的,因为只有在签名的int i溢出(即UB(时才可以终止循环,因此,编译器可以例如将其优化到无限的循环中(Clang在-O3上使用(,或者做其他编译器各种时髦的东西。我现在的问题是:从我对C 标准的读取中,等于签名的类型 May 别名(即Pointers int*unsigned* May May May(。为了进行一些时髦的签名"包装",以下行为是否不确定?

#include <iostream>
static int safe_inc(int a)
{
    ++reinterpret_cast<unsigned&>(a);
    return a;
}
void func() {
    int i = 2147483640;
    while (i < safe_inc(i))
    {
        std::cerr << i << 'n';
        ++i;
    }
    return;
}
int main() {
    func(); 
}

我在-O3上使用-Wall -Wextra -Wpedantic -O3 -fsanitize=address,undefined参数的Clang 8和GCC 9尝试了上述代码,并且没有任何错误或警告,并且循环在包装到INT_MIN后终止。

cppreference.com告诉我

类型aleiasing

每当尝试通过类型AliasedType的glvalue读取或修改类型Dynamictype对象的存储值时,该行为是不确定的,除非以下一个是正确的:

  • AliasedType是Dynamictype的签名或未签名的变体(可能是CV合格的(。

从我的阅读中,这意味着出于类型混叠的目的,不考虑签名,并且使用reinterpret_cast的代码具有明确的语义(尽管有些俗气(。

在这里混音是完全合法的。请参阅http://eel.is/c draft/expr.prop#basic.lval-11.2:

如果程序试图通过一个与其中之一的Glvalue([Cons.Qual](的类型不相似([Cons.Qual](以下类型的行为不确定:53

(11.1(对象的动态类型

(11.2(a 类型是签名或无符号类型对应于对象的动态类型

我认为,也值得谈论实际的溢出问题,这不一定需要reinterpret_cast。通过隐式积分转换

,可以实现同样的效果
 unsigned x = i;
 ++x;
 i = x; // this would serve you just fine.

此代码将是实现定义的pre-c 20,因为您将从无法用目标类型表示的值转换。

由于C 20此代码将被很好地形成。

请参阅https://en.cppreference.com/w/cpp/language/implitic_conversion

在旁注上,如果要整数溢出语义,则不妨从未签名类型开始。

您的代码完全合法,CPP参考是一个很好的来源。您可以在标准[basic.lval]/11

中找到相同的信息

如果程序试图通过glvalue访问对象的存储值,该glvalue的类型与以下类型不相似([cons.qual](,则行为不确定:

  • 对象的动态类型

  • 一种类型是对象的动态类型的签名或无符号类型,[...]