Visual C++是否认为有符号整数溢出未定义

Does Visual C++ consider signed integer overflow undefined?

本文关键字:符号 整数 溢出 未定义 C++ 否认 Visual      更新时间:2023-10-16

最近,有符号整数溢出在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的情况。具体地,如果nINT_MAX,则乘法将如同nlong 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编译器上可能没有(。相反,您可能正在调用未定义的越界指针算术。