Jon*_*ler 12 c++ signed memcpy language-lawyer reinterpret-cast
采取以下代码
#include <iostream>
void func() {
int i = 2147483640;
while (i < i + 1)
{
std::cerr << i << '\n';
++i;
}
return;
}
int main() {
func();
}
Run Code Online (Sandbox Code Playgroud)
这段代码显然是错误的,因为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();
}
Run Code Online (Sandbox Code Playgroud)
我已经在Clang 8和GCC 9上-O3使用-Wall -Wextra -Wpedantic -O3 -fsanitize=address,undefined参数尝试了上面的代码,没有错误或警告,并且循环到包装后终止INT_MIN。
类型别名
每当尝试通过AliasedType类型的glvalue读取或修改DynamicType类型的对象的存储值时,除非满足以下条件之一,否则行为是不确定的:
- AliasedType是DynamicType的(可能是cv限定的)带符号或无符号的变体。
根据我的阅读,这意味着出于类型别名的目的,不考虑符号性,并且所使用的代码reinterpret_cast具有定义明确的语义(尽管无论如何还是有些俗气)。
在这里别名是完全合法的。参见http://eel.is/c++draft/expr.prop#basic.lval-11.2:
如果程序试图通过glvalue访问对象的存储值,该glvalue的类型与以下一种类型不相似([conv.qual]),则行为未定义:53
(11.1)对象的动态类型,
(11.2)是与对象的动态类型相对应的有符号或无符号类型的类型
我认为,也不一定要讨论实际的溢出问题reinterpret_cast。隐式积分转换可以达到完全相同的效果
unsigned x = i;
++x;
i = x; // this would serve you just fine.
Run Code Online (Sandbox Code Playgroud)
该代码将在C ++ 20之前的实现中定义,因为您将从无法用目标类型表示的值进行转换。
从C ++ 20开始,此代码将格式正确。
参见https://en.cppreference.com/w/cpp/language/implicit_conversion
附带说明一下,如果您希望整数溢出语义,则最好从无符号类型开始。
您的代码完全合法,cpp参考是一个很好的来源。您可以在标准[basic.lval] / 11中找到相同的信息
如果程序尝试通过其类型与以下类型之一不相似([conv.qual])的glvalue访问对象的存储值,则行为未定义:
对象的动态类型,
一种类型,它是与对象的动态类型相对应的有符号或无符号类型,[...]
| 归档时间: |
|
| 查看次数: |
262 次 |
| 最近记录: |