【问题标题】:Using volatile qualifier suppresses compiler warning使用 volatile 限定符会抑制编译器警告
【发布时间】:2024-01-05 12:37:01
【问题描述】:

今天我正在审查一个人的代码,他声明了一个变量volatile。在询问它时,他告诉它在某些系统上会产生奇怪的行为。

在删除 volatile 和编译时,它产生了这个编译器警告

iteration 2 invokes undefined behavior [-Waggressive-loop-optimizations]

代码与下面的代码非常相似,数组被越界访问。 由于他使用的是不同的代码库,其中 Makefile 不同,因此该警告不会在他的系统上产生。

int a[4]={1,2,3,4};
int i;   //when declared volatile int i, doesn't produce warning
i=0;
while(i<5) {
    printf("%d\t", a[i]);    //a[4] will invoke undefined behavior
    i+=2;   
}

现在,我无法弄清楚两件事:

  1. 我应该启用哪些确切的 gcc 标志才能收到此警告?
  2. 为什么将 i 声明为 volatile 会抑制该警告?

【问题讨论】:

    标签: c volatile


    【解决方案1】:

    当激进循环优化看到如下代码...

    int i;
    i=0;
    while(i<5) {
        printf("%d\t", a[i]);
        i+=2;   
    }
    

    ...它将使用一种称为“循环展开”的技术来重写它...

    printf("%d\t", a[0]);
    printf("%d\t", a[2]);
    printf("%d\t", a[4]);
    

    问题!迭代 0 和 1 很好,但迭代 2 将执行越界数组访问,调用未定义的行为。这就是你收到警告的原因。

    i 声明为volatile 会阻止编译器进行这种优化(因为它不能确定另一个进程在循环执行期间没有修改i 的值),所以它必须离开代码原样。你仍然有未定义的行为,只是编译器没有警告你。总而言之,你的同事给了你一个糟糕的“修复”。

    【讨论】:

    • 很好解释。谢谢
    【解决方案2】:

    您的未定义行为是您的循环允许 i=4,这将读取到数组的末尾。循环优化器会注意到这一点,但不管优化如何,这当然是个问题。

    volatile 告诉编译器i 的值可以从这段代码之外更改。这样做的实际效果是编译器无法进行依赖于假设i 的值的优化。这会关闭注意到您的问题的优化。

    仅使用volatile 来绕过警告是一种糟糕的做法。而是将while 条件更改为while (i &lt; 4)

    【讨论】:

      【解决方案3】:
      1. 警告告诉您哪个标志启用它:-Waggressive-loop-optimizations
      2. 声明变量volatile 意味着编译器必须假定编译器无法控制的事物可能会检查和修改变量。因此它不能假设i 将永远采用4 的值(或者i += 2 将始终将2 加到i 的值等)。

      【讨论】:

      • -Waggressive-loop-optimizations 是我尝试的第一件事,没有运气
      • @Raman 您是否也启用了一般优化? IE。类似-O-O2
      • 是的,试过了。但似乎问题与 gcc 版本有关。
      • 使用最新版本的 gcc 和 -O 解决了这个问题。谢谢。
      最近更新 更多