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

why do I get different results with the same conditions in a for loop?

我在尝试使用 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 = 10n = 11。而且我发现获取n的值的时间比m.

长很多

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

a.size() 返回的值是 size_t 类型,它是一个 unsigned int,因为没有任何理由让大小为负数。如果您使用无符号数执行 1-2,它将翻转并成为接近无符号 int 最大值的值,并且循环将花费相当长的时间 运行,或者甚至可能不会停止,因为有符号整数可以' 大于无符号值的上半部分。这取决于比较有符号和无符号的规则,我当场记不太清了。

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

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

这里,2 - 1是-1,在32位系统上换行到2^32 - 1。当您尝试从 10 中减去 2^32 - 1 时,会导致有符号整数下溢,因为 32 位整数的最小值为 -2^31。有符号整数 overflow/underflow 是 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.

再次强调,有符号整数 overflow/underflow 是未定义的行为,因此当发生奇怪的事情时不要感到惊讶,尤其是在现代编译器优化的情况下。例如,您的整个程序可以 "optimized" 完全不做任何事情,因为它总是会调用 UB。您甚至不能保证看到 std::cout<<m<<endl; 的输出,即使在该行执行后调用了 UB。