【问题标题】:why do I get different results with the same conditions in a for loop?为什么我在 for 循环中在相同条件下得到不同的结果?
【发布时间】:2018-08-27 03:26:27
【问题描述】:

当我尝试使用 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() 是有符号类型,a.size() - 2 将导致 -1。由于a.size() 是无符号类型,a.size() - 2 会产生一个非常大的正数。示例ideone.com/d2azlC.
  • 那你能告诉我为什么它会导致这么大的正数,为什么 n 仅仅等于 11?@RSahu
  • 我会让人解释的。恐怕我脑子里还没有完全清楚。
  • 当你遇到这样一个令人惊讶的结果时,第一步是检查你的假设。例如,添加std::cout &lt;&lt; a.size() - 2 &lt;&lt;endl; 以验证它确实是-1。然后你就可以开始弄清楚为什么不是这样了。

标签: c++ for-loop signed


【解决方案1】:

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。你甚至不能保证看到std::cout&lt;&lt;m&lt;&lt;endl; 的输出,即使在该行执行后调用了 UB。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-21
      • 2022-12-18
      • 1970-01-01
      相关资源
      最近更新 更多