为什么这(i = ++ i%3)会产生警告:“可能未定义”?

int main(void) { int i = 0; i = ++i % 3; return 0; } 

我这样编译:

 $ gcc -Wall main.c -o main main.c: In function 'main': main.c:4: warning: operation on 'i' may be undefined 

为什么编译器说i可能未定义?

正如其他人所指出的那样,行为是不确定的 :

6.5表达式

2在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。 72)此外,先前的值应该是只读的,以确定要存储的值。 73)

72)浮点状态标志不是对象,可以在表达式中多次设置。 73)此段落呈现未定义的语句表达式,如

     i = ++ i + 1;
     a [i ++] = i;

同时允许

     i = i + 1;
     a [i] = i;

表达式i = ++i % 3尝试在下一个序列点(在本例中为;结束语句)之前两次修改i包含的值,一次通过评估++i ,一次通过评估更大的赋值表达式。

现在,为什么这会成为问题? 毕竟,C#和Java可以很好地处理这些表达式。

问题在于,除了极少数例外,C不保证表达式中的操作数以任何特定顺序进行计算,或者表达式的副作用将在计算表达式后立即应用(与C#和Java不同,做那些保证)。 例如,表达式++i具有结果i + 1)和副作用 (增加存储在i的值); 但是,可以推迟副作用,直到评估出更大的表达。 IOW,允许以下一系列操作:

     t0 = i + 1
     t1 = t0%3
     i = t1
     i = i + 1

Oopsie。 不是我们想要的。

这是一个刻意的设计决定; 这个想法是它允许编译器以最佳方式重新排序评估(通过利用已经存在于寄存器中的值)。 缺点是某些表达式组合会产生不可预测的结果。

因为您正在修改i的值两次而没有插入序列点 。 这是未定义的行为 。

在标准中,它是未定义的行为,因为i被修改了两次没有插入序列点。

 i = ++i % 3; 

但这不是重点。 真正的问题是: 为什么有人会写这样的代码?!

你想要i有什么价值? 如果你用i = ...i分配一个全新的值,你试图用++i实现什么效果? 如果这是parallel-universe-C,其中这样的代码实际上有意义,那么 – 在最好的情况下 – 递增的i立即被替换为分配的全新值。 那么为什么不把它写成

 i = (i+1) % 3; 

这在C-as-know-know-it中也是正确的。