【问题标题】:Printing zero but not exiting loop based on that condition根据该条件打印零但不退出循环
【发布时间】:2017-07-23 23:54:23
【问题描述】:

考虑以下代码:

#include <string>
#include <iostream>

int f(int n)
{
    int i = 0;
    for (i = 0; n < 0 || n > 0; n *= 2, ++i) {
        std::cout << "n = " << n << std::endl;
    }
    return i;
}

int main(int argc, char* argv[])
{
    return f(argc > 1 ? std::stol(argv[1]) : 1) != 0;
}

在 clang++ 上它输出:

n = 1
n = 2
n = 4
n = 8
n = 16
n = 32
n = 64
n = 128
n = 256
n = 512
n = 1024
n = 2048
n = 4096
n = 8192
n = 16384
n = 32768
n = 65536
n = 131072
n = 262144
n = 524288
n = 1048576
n = 2097152
n = 4194304
n = 8388608
n = 16777216
n = 33554432
n = 67108864
n = 134217728
n = 268435456
n = 536870912
n = 1073741824
n = -2147483648
n = 0

在 g++5 和 g++6 上输出:

n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
n = 0
...

这是……一个有趣的行为。怎么了?是编译器的bug吗?

【问题讨论】:

  • 优化级别会影响结果吗?
  • 是的。它似乎适用于-O1。不适用于 -O2/-O3。
  • 程序的行为因argv[1]而异;您应该指定哪个输入导致发布的输出
  • 然后看看用-g编译后的objdump,看看那个循环有什么不同

标签: loops c++11 g++ undefined-behavior integer-overflow


【解决方案1】:

除非使用参数0 调用程序,否则使用非零参数调用函数f

循环迭代步骤n *= 2最终会导致整数溢出,即undefined behaviour。这是程序的错误,不是编译器的错误。

【讨论】:

  • @Vincent 这意味着任何事情都有可能发生;过去常用的例子“恶魔可能会飞出你的鼻子”
  • 我认为编译器已经确定 n 将溢出(由于循环没有其他退出条件),因此将在未来某个时间产生未定义的行为。当 n * 2 (例如 n=1) 没有溢出时,编译器是否可以有效地调用未定义的行为,因为 1*2 的结果不是未定义的?所以是的,程序有一个错误,但编译器也有一个吗?如果编译器可以确定程序中将发生未定义的行为,是否允许在生成它的事件实际发生之前调用未定义的行为?
  • @mattnz “未定义的行为”是程序的一个属性,它不是编译器必须调用的东西。 (如果编译器总能检测到 UB,那么就不需要 UB 的概念了!)。 This thread可能会回答你的问题
猜你喜欢
  • 2014-09-03
  • 1970-01-01
  • 2017-12-14
  • 2018-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-07
  • 2014-08-14
相关资源
最近更新 更多