8

我有以下代码:

#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 >= 1 失败。相反,测试以某种方式通过了。这是编译器错误,还是我在某处做了未定义的事情?

4

4 回答 4

8

我记得在 2001 年遇到过这个问题。我很惊讶它仍然存在。是的,这是一个编译器错误。

优化师看到

i + 1 >= 1;

从理论上讲,我们可以通过将所有常量放在同一侧来优化它:

i >= (1-1);

因为i是无符号的,所以它总是大于或等于零。

请在此处查看此新闻组讨论。

于 2009-03-24T02:44:59.237 回答
4

ISO14882:2003,第 5 节,第 5 段:

如果在计算表达式期间,结果未在数学上定义或不在其 type 的可表示值范围内,则行为未定义,除非此类表达式是常量表达式 (5.19),在这种情况下程序有病-形成。

(强调我的。)所以,是的,行为是未定义的。该标准不保证整数上溢/下溢情况下的行为。

编辑:该标准在其他地方的问题上似乎略有冲突。

第 3.9.1.4 节说:

声明为无符号的无符号整数应遵守算术模 2 n 的定律,其中 n 是该特定大小的整数的值表示中的位数。

但是第 4.7.2 和 .3 节说:

2) 如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2 n,其中 n 是用于表示无符号类型的位数)。[注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。]

3) 如果目的类型是有符号的,如果能以目的类型(和位域宽度)表示,则值不变;否则,该值是实现定义的。

(强调我的。)

于 2009-03-24T02:52:47.610 回答
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; )
于 2009-03-24T02:50:47.070 回答
0

是的,我刚刚在 Visual Studio 2005 上对此进行了测试,它在 Debug 和 Release 中的行为肯定不同。我想知道2008年是否修复了它。

有趣的是,它抱怨您从 size_t(.length 的结果)到 unsigned int 的隐式转换,但生成错误代码没有问题。

于 2009-03-24T02:55:23.750 回答