为什么在for循环中在相同条件下会得到不同的结果?

时间:2018-08-27 03:26:27

标签: c++ for-loop signed

当我尝试使用for循环来解决问题时被困住了。

这是我的简化代码:

int main(int argc, const char * argv[])
{
    std::vector<int> a;
    a.push_back(2333);
    int n = 10, m = 10;
    for(int i=0; i< -1; i++)
        m--;
    std::cout<<m<<endl;
    for(int j=0; j<a.size()-2; j++)
        n--;
    std::cout<<n<<endl;
    return 0;
}

显然,a.size() = 1因此这两个结束条件应该相同。但是,当我在Xcode 9.4.1上运行代码时,却意外地发现m = 10  和n = 11。而且我发现获得n的值所花费的时间比m更长。

为什么会得到这样的结果?任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:2)

size()返回的值为std::size_t,这是一个无符号整数类型。这意味着它只能表示非负数,并且如果您执行的运算结果为负数,则它将像模块化算术一样环绕到尽可能大的值。

在这里,2 - 1为-1,在32位系统上,该字符串包装为2^32 - 1。当尝试从10中减去2^32 - 1时,由于32位整数的最小值为-2^31,将导致有符号整数下溢。有符号整数上溢/下溢为undefined behavior,因此任何事情都可能发生。

在这种情况下,下溢似乎缠绕到最大值,就像带符号的整数一样。因此结果将是10 - (2^32 - 1) + 2^32,即11。我们添加2^32以模拟下溢环绕。换句话说,在循环的第2^31 + 10次迭代之后,n是32位整数中的最小可能值。下一次迭代将导致环绕,因此n现在为2^31 - 1。然后,其余的2^31 - 12迭代将n减少到11。

同样,有符号整数上溢/下溢是未定义的行为,因此,当发生某些奇怪的事情时,不要感到惊讶,尤其是对于现代编译器优化而言。例如,您的整个程序可以被“优化”,以使其完全不执行任何操作,因为它将始终调用UB。即使在该行执行之后调用了UB,您甚至不能保证看到std::cout<<m<<endl;的输出。

答案 1 :(得分:0)

a.size()返回的值是类型size_t,它是一个无符号的int,因为没有任何理由使它的大小为负数。如果您对无符号数字执行1-2,则它将翻转并成为接近无符号int最大值的值,并且循环将需要相当长的一段时间才能运行,甚至可能因为有符号整数不能更大而停止比无符号值的上半部分大。这取决于比较签名和未签名的规则,我当时不确定该规则。

使用调试器并确保类型正确(您的编译器应在此处提及带符号/无符号不匹配)有助于确定这些情况。