Visual C++是否认为有符号整数溢出未定义
Does Visual C++ consider signed integer overflow undefined?
最近,有符号整数溢出在C和C++中是官方未定义的,这引起了很多关注。然而,给定的实现可以选择定义它;在C++中,一个实现可以将std::numeric_limits<signed T>::is_modulo
设置为true
,以指示有符号整数溢出是为该类型定义的,并像无符号整数一样进行包装。
Visual C++将std::numeric_limits<signed int>::is_modulo
设置为true
。这几乎不是一个可靠的指标,因为GCC多年来一直将其设置为true,并且存在未定义的有符号溢出。直到本周早些时候,我还从未遇到过Visual C++的优化器除了为有符号整数提供环绕行为之外什么都没做的情况。
我发现了一种情况,在这种情况下,如果将INT_MAX
的值传递给特定函数,优化器会发出x86-64程序集代码,但这些代码的行为不正确。我不知道这是否是一个错误,因为Visual C++似乎没有说明是否定义了有符号整数溢出。所以我想知道,它应该在Visual C++中定义吗?
编辑:我在读到Visual C++2013 Update 2中一个不在Update 1中的严重错误时发现了这一点,如果启用了优化,下面的循环会生成错误的机器代码:
void func (int *b, int n)
{
for (int i = 0; i < n; i++)
b[i * (n + 1)] = 1;
}
Update2错误导致重复的行将其代码生成为b[i] = 1;
,这显然是错误的。它变成了rep stosd
。
真正有趣的是,以前的版本Update 1中有一些奇怪之处。它生成的代码没有正确处理n
恰好等于INT_MAX
的情况。具体地,如果n
是INT_MAX
,则乘法将如同n
是long long
而不是int
一样——换句话说,加法n + 1
将不会导致结果变成INT_MIN
。
这是更新1:中的程序集代码
movsxd rax, edx ; RDX = 0x000000007FFFFFFF; RAX = 0x000000007FFFFFFF.
test edx, edx
jle short locret_76 ; Branch not taken, because EDX is nonnegative.
lea rdx, ds:4[rax*4] ; RDX = RAX * 4 + 4; RDX becomes 0x0000000200000000.
nop ; But it's wrong. RDX should now be 0xFFFFFFFE00000000.
loc_68:
mov dword ptr [rcx], 1
add rcx, rdx
dec rax
jnz short loc_68
locret_76:
retn
问题是,我不知道这是否是编译器错误——在GCC和Clang中,这不会是编译器错误,因为这些编译器认为有符号整数上溢/下溢是未定义的。这是否是Visual C++中的错误取决于Visual C++是否认为有符号整数上溢/下溢是未定义的。
除此之外,我看到的其他所有案例都显示Visual C++考虑定义有符号的上溢/下溢,因此这是个谜。
发现2016年的一个有趣的花絮(VS2015更新3(:
他们谈到了他们想引入VS2015:的新SSA优化器
C++团队博客-介绍一个新的、高级的Visual C++代码优化器
从历史上看,Visual C++并没有利用C和C++标准考虑溢出符号的结果操作未定义。其他编译器在这方面非常积极这促使决定实施一些模式利用未定义的整数溢出行为。我们实施了我们认为是安全的,没有强加任何不必要的东西生成的代码中存在安全风险。
所以你有它。我读到:"我们从未在任何额外的位中编程来利用这个UB",但从VS2015/Update3开始,我们将有一些。
我应该注意的是,即使在那之前,我也会非常谨慎,因为对于64位代码和32位变量,如果编译器/优化器只是将32位带符号的int放入64位寄存器中,那么无论如何都会有未定义的内容。(如"如何不编码:未定义的行为比你想象的更接近"所示——不幸的是,从博客文章中不清楚他是使用VS2015之前还是之后的Update3。(
因此,我对整个事件的看法是,MSVC总是认为是UB,尽管过去的优化器版本并没有利用的特殊优势。新的SAA优化器似乎可以做到这一点。(测试–d2UndefIntOverflow–
开关是否能完成它的工作会很有趣。(
您的示例可能对n == INT_MAX
有未定义的行为,但这不仅仅是因为有符号整数溢出未定义(Microsoft编译器上可能没有(。相反,您可能正在调用未定义的越界指针算术。
- 检查TCHAR数组输入是否为带符号整数C++
- 如何打印boost多精度128位无符号整数
- C++模板函数,用于比较任何无符号整数和有符号整数
- 为什么乘以常量有符号整数分数没有优化?
- 在线程中读取无符号整数时,c++ 位是否以原子方式切换?
- FlatBuffers/Protobuf 中是否有支持任意 24 位有符号整数定义的可移植二进制序列化架构?
- C++11 标准是否保证零值有符号整数的一元减号为零?
- Constexpr 可变参数模板,用于对无符号整数进行重新排序
- 为什么 Clang 和 GCC 中两个无符号整数之和的结果类型不同
- 为什么对无符号字符进行算术运算会将它们提升为有符号整数
- 从 std::string 转换为 const 无符号整数
- 迭代器和无符号整数的重载 + 运算符
- C++,概念不适用于无符号整数作为结果类型?
- 在C++中,将无符号整数转换为八进制表示,反之亦然的最佳方法是什么
- 原子式清除无符号整数的最低非零位
- 什么是 16 字节有符号整数数据类型?
- 计算机使用什么方法添加无符号整数
- 运行时错误:有符号整数溢出:964632435 * 10 无法在类型 'int' 中表示
- boost::任何带有结构体和无符号整数
- 添加有符号和无符号整数