使用 volatile 限定符抑制编译器警告

Using volatile qualifier suppresses compiler warning

今天我正在审查一个人的代码,他在其中声明了一个变量 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 会抑制该警告?
  1. 警告告诉您哪个标志启用它:-Waggressive-loop-optimizations
  2. 声明一个变量volatile意味着编译器必须假定编译器控制之外的事物可能会检查和修改该变量。因此它不能假定 i 永远取值 4(或者 i += 2 总是将 2 添加到 i 的值,等等)。

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

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

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

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

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

...它会使用一种叫做"loop unrolling"的技术来重写它...

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

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

i 声明为 volatile 会阻止编译器进行此优化(因为无法确定另一个进程在循环期间未修改 i 的值执行),所以它必须保持代码原样。你仍然有未定义的行为,只是编译器没有警告你。总而言之,来自你同事的糟糕"fix"。