【问题标题】:Erroneous Code + GCC 5.4 Optimisation Causes Infinite Loop错误代码 + GCC 5.4 优化导致无限循环
【发布时间】:2017-05-11 01:54:37
【问题描述】:

免责声明:

  • 我在这里分享的代码是真实代码的独立版本,但会重现相同的行为。

以下代码使用 gcc 5.4.0 编译并在 Ubuntu 16.04 上启用优化,执行时会生成无限循环:

#include <stdio.h>


void *loop(char *filename){

    int counter = 10;
    int level = 0;
    char *filenames[10];
    filenames[0] = filename;

    while (counter-- > 0) {

        level++;
        if (level > 10) {
            break;
        }

        printf("Level %d - MAX_LEVELS %d\n", level, 10);
        filenames[level] = filename;

    }

    return NULL;
}

int main(int argc, char *argv[]) {
    loop(argv[0]);
}

编译器版本:

gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.

使用的编译命令:

gcc infinite.c -O2  -o infinite

我知道这是由优化标志“-02”引起的,因为没有它就不会发生。我也知道将 volatile 添加到变量“level”也可以修复错误。但我不能将此关键字添加到我的所有变量中。

我的问题是,为什么会发生这种情况,我以后可以做些什么来避免它?

是否有任何 gcc 标志仍然在 -O2 的类似级别上优化代码而没有此类问题?

【问题讨论】:

  • 您的行为未定义,因为filenames[level] = filename; 可以访问无效内存,因为level 可以是10
  • 正如其他人所说,文章的标题应该是“错误的程序导致无限循环”。当程序调用未定义的行为时,所有行为都是“正确的”。
  • 从 loop() 返回 NULL,但从 main() 调用 loop() 函数从不分配返回值 - 这对 UB 有影响吗?还是这只是被遗忘的返回值?
  • @Nguaial 不同版本的编译器使用不同的优化策略。对于 GCC,通常版本号越高,优化完成的越多或做得越好。
  • 是否修改代码使if (level &gt; 10) 更改为if (level &gt; 9) 解决问题?

标签: c gcc optimization


【解决方案1】:

您发现了一个未定义行为的示例,该示例导致优化器执行了意想不到的操作! GCC 可以看到,如果循环运行 10 次,则存在未定义的行为。

此代码将写入filenames[1]filenames[10],即filenames 的第2 到第11 个元素。但是 filenames 有 10 个元素长,所以最后一个是未定义的行为。

因为不允许你有未定义的行为,它可以假设循环将在到达 10 之前以其他方式停止(也许你有一个修改版本的 printf 将调用 exit?)。

它发现如果循环在到达 10 之前无论如何都要停止,那么让代码让它只运行 10 次是没有意义的。所以它会删除该代码。

【讨论】:

  • 这是有道理的。将代码从 if (level > 10) 更改为 if (level > 9) 可以解决问题。
【解决方案2】:

要防止这种优化,您可以使用“-fno-aggressive-loop-optimizations”。

【讨论】:

    猜你喜欢
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 2015-04-22
    • 2014-01-17
    相关资源
    最近更新 更多