使用 reinterpret_cast 的符号别名
Signedness aliasing using reinterpret_cast
取下面代码
#include <iostream>
void func() {
int i = 2147483640;
while (i < i + 1)
{
std::cerr << i << '\n';
++i;
}
return;
}
int main() {
func();
}
这段代码显然是错误的,因为 while 循环只有在带符号的 int i
溢出时才能终止,这是 UB,因此编译器可能会将其优化为无限循环(Clang 在-O3
),或者做其他时髦的事情。我现在的问题是:根据我对 C++ 标准的阅读,等同于符号 的类型可能 别名(即指针 int*
和 unsigned*
可能是别名)。为了做一些时髦的签名“包装”,以下是否有未定义的行为?
#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
上使用 Clang 8 和 GCC 9 以及 -Wall -Wextra -Wpedantic -O3 -fsanitize=address,undefined
参数尝试了上述代码,没有收到任何错误或警告,并且循环在包装到 INT_MIN
后终止。
cppreference.com 告诉我
Type aliasing
Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:
- AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.
根据我的阅读,这意味着出于类型别名的目的,不考虑符号性,并且使用 reinterpret_cast
的代码具有明确定义的语义(尽管无论如何有点俗气)。
在这里使用别名是完全合法的。见 http://eel.is/c++draft/expr.prop#basic.lval-11.2:
If a program attempts to access the stored value of an object through
a glvalue whose type is not similar ([conv.qual]) to one of the
following types the behavior is undefined:53
(11.1) the dynamic type of the object,
(11.2) a type that is the signed or unsigned type
corresponding to the dynamic type of the object
我觉得,其实溢出问题也值得一谈,不一定需要reinterpret_cast
。使用隐式整数转换可以实现完全相同的效果
unsigned x = i;
++x;
i = x; // this would serve you just fine.
此代码将由实现定义 pre-C++20,因为您将从目标类型无法表示的值进行转换。
自 C++20 起,此代码将为 well-formed。
见https://en.cppreference.com/w/cpp/language/implicit_conversion
附带说明一下,如果您想要整数溢出语义,不妨从无符号类型开始。
您的代码完全合法,cpp 参考是一个很好的来源。您可以在标准 [basic.lval]/11
中找到相同的信息
If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:
the dynamic type of the object,
a type that is the signed or unsigned type corresponding to the dynamic type of the object,[...]
取下面代码
#include <iostream>
void func() {
int i = 2147483640;
while (i < i + 1)
{
std::cerr << i << '\n';
++i;
}
return;
}
int main() {
func();
}
这段代码显然是错误的,因为 while 循环只有在带符号的 int i
溢出时才能终止,这是 UB,因此编译器可能会将其优化为无限循环(Clang 在-O3
),或者做其他时髦的事情。我现在的问题是:根据我对 C++ 标准的阅读,等同于符号 的类型可能 别名(即指针 int*
和 unsigned*
可能是别名)。为了做一些时髦的签名“包装”,以下是否有未定义的行为?
#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
上使用 Clang 8 和 GCC 9 以及 -Wall -Wextra -Wpedantic -O3 -fsanitize=address,undefined
参数尝试了上述代码,没有收到任何错误或警告,并且循环在包装到 INT_MIN
后终止。
cppreference.com 告诉我
Type aliasing
Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:
- AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.
根据我的阅读,这意味着出于类型别名的目的,不考虑符号性,并且使用 reinterpret_cast
的代码具有明确定义的语义(尽管无论如何有点俗气)。
在这里使用别名是完全合法的。见 http://eel.is/c++draft/expr.prop#basic.lval-11.2:
If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:53
(11.1) the dynamic type of the object,
(11.2) a type that is the signed or unsigned type corresponding to the dynamic type of the object
我觉得,其实溢出问题也值得一谈,不一定需要reinterpret_cast
。使用隐式整数转换可以实现完全相同的效果
unsigned x = i;
++x;
i = x; // this would serve you just fine.
此代码将由实现定义 pre-C++20,因为您将从目标类型无法表示的值进行转换。
自 C++20 起,此代码将为 well-formed。
见https://en.cppreference.com/w/cpp/language/implicit_conversion
附带说明一下,如果您想要整数溢出语义,不妨从无符号类型开始。
您的代码完全合法,cpp 参考是一个很好的来源。您可以在标准 [basic.lval]/11
中找到相同的信息If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:
the dynamic type of the object,
a type that is the signed or unsigned type corresponding to the dynamic type of the object,[...]