最近得到了很多关注,在C和C ++中正式未定义有符号整数溢出。但是,给定的实现可以选择定义它;在C ++中,实现可以将std::numeric_limits<signed T>::is_modulo
设置为true
以指示已为该类型定义了有符号整数溢出,并且像无符号整数一样包装。
Visual C ++将std::numeric_limits<signed int>::is_modulo
设置为true
。这几乎不是一个可靠的指标,因为GCC多年来将其设置为真,并且未定义签名溢出。我从来没有遇到过一个案例,其中Visual C ++的优化器已经完成了任何事情,但是给签名整数提供了环绕行为 - 直到本周早些时候。
我发现了一个案例,如果精确INT_MAX
的值传递给特定函数,优化器会发出x86-64汇编代码,这些代码行为不正确。我不知道它是否是一个错误,因为Visual C ++似乎没有说明是否认为已经定义了有符号整数溢出。所以我想知道,它应该在Visual C ++中定义吗?
void func (int *b, int n)
{
for (int i = 0; i < n; i++)
b[i * (n + 1)] = 1;
}
更新2错误会导致重复行生成代码,就好像它是b[i] = 1;
一样,这显然是错误的。它变成rep stosd
。
真正有趣的是之前的版本更新1中存在奇怪。它生成的代码无法正确处理n
完全等于{{1}的情况}。具体来说,如果INT_MAX
为n
,则乘法就像INT_MAX
n
而不是long long
一样 - 换句话说,加法int
}不会导致结果变为n + 1
。
这是Update 1中的汇编代码:
INT_MIN
问题是我不知道这是否是编译器错误 - 在GCC和Clang中,这不会是编译器错误,因为那些编译器认为有符号整数溢出/下溢是未定义的。这是否是Visual C ++中的错误取决于Visual C ++是否认为有符号整数溢出/下溢未定义。
除了这个之外我见过的其他每个案例都显示Visual C ++要考虑定义已签名的溢出/下溢,因此是个谜。
答案 0 :(得分:1)
您的示例可能确实具有n == INT_MAX
的未定义行为,但不仅仅是因为未定义的有符号整数溢出(它可能不在Microsoft编译器上)。相反,您可能正在调用未定义的越界指针算法。
答案 1 :(得分:1)
在2016年(VS2015 Update 3)中发现了一个有趣的花絮:
他们谈论了他们想要引入VS2015的新SSA优化器:
C ++团队博客-引入了新的高级Visual C ++代码优化器
... ... ...
从历史上看,Visual C ++并未没有 利用这一事实, C和C ++标准考虑了符号溢出的结果 操作未定义。其他编译器对此非常激进 方面,这促使人们决定实施某些模式 利用未定义的整数溢出行为。我们实施了 我们认为是安全的,没有施加任何不必要的 生成的代码中存在安全风险。
因此,您已经拥有了它。我读为:“我们从不编程任何额外的位来使用此UB”,但是从VS2015 / Update3开始,我们将拥有一些。
我应该注意,即使在此之前,我也要格外警惕,因为对于64位代码和32位变量,如果编译器/优化器只是将32位带符号的int放入64位寄存器中,则无论如何。 (如"How not to code: Undefined behavior is closer than you think"中所示-不幸的是,从博客文章中尚不清楚他是使用VS2015还是更新3。)
因此,我对整个事件的看法是,即使过去的优化程序版本没有利用 special 的事实,MSVC始终将其视为UB。新的SAA优化器似乎可以肯定地做到这一点。 (测试–d2UndefIntOverflow–
开关是否能正常工作会很有趣。)