我有以下代码:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
string a = "a";
for(unsigned int i=a.length()-1; i+1 >= 1; --i)
{
if(i >= a.length())
{
cerr << (signed int)i << "?" << endl;
return 0;
}
}
}
如果我使用完全优化在MSVC中编译,我得到的输出是“-1?”。如果我在调试模式下编译(没有优化),我没有输出(预期。)
我认为标准保证无符号整数以可预测的方式溢出,因此当i =(unsigned int)( - 1)时,i + 1 = 0,并且循环条件i + 1&gt; = 1失败。相反,测试以某种方式通过。这是一个编译器错误,还是我在某处做某些未定义的事情?
答案 0 :(得分:8)
我记得在2001年遇到这个问题。我很惊讶它仍然存在。是的,这是一个编译器错误。
优化者正在看
i + 1 >= 1;
理论上,我们可以通过将所有常量放在同一侧来优化这一点:
i >= (1-1);
因为 i 是无符号的,所以它总是大于或等于零。
请参阅此新闻组讨论here。
答案 1 :(得分:4)
ISO14882:2003,第5节,第5段:
如果在评估表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义,除非此类表达式是常量表达式( 5.19),在这种情况下,该程序是不正确的。
(强调我的。)所以,是的,行为是未定义的。在整数溢出/下溢的情况下,该标准不保证行为。
编辑:标准似乎在其他地方略有冲突。
第3.9.1.4节说:
无符号整数,声明为无符号整数,应遵守算术模2 n的定律,其中n是该特定整数大小的值表示中的位数。
但第4.7.2和.3节说:
2)如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模2 n,其中n是用于表示无符号类型的位数)。 [注意:在二进制补码表示中,此转换是概念性的,并且位模式没有变化(如果没有截断)。 ]
3)如果目标类型是有符号的,如果它可以在目标类型(和位字段宽度)中表示,则该值不变; 否则,该值是实现定义的。
(强调我的。)
答案 2 :(得分:1)
我不确定,但我认为你可能犯了一个错误。
我怀疑问题在于编译器如何处理for
控件。我可以想象优化器在做什么:
for(unsigned int i=a.length()-1; i+1 >= 1; --i) // As written
for (unsigned int i = a.length()-1; i >= 0; --i) // Noting 1 appears twice
for (unsigned int i = a.length()-1; ; --i) // Because i >= 0 at all times
这是否正在发生的事情是另一回事,但它可能足以混淆优化器。
使用更标准的循环配方可能会更好:
for (unsigned i = a.length()-1; i-- > 0; )
答案 3 :(得分:0)
是的,我刚刚在Visual Studio 2005上对此进行了测试,它在Debug和Release中的表现肯定不同。我想知道2008年是否会修复它。
有趣的是,它抱怨你从size_t(.length的结果)到unsigned int的隐式转换,但生成错误代码没有问题。