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

时间:2011-10-24 14:33:55

标签: c

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可能未定义?

3 个答案:

答案 0 :(得分:11)

因为您在没有干预sequence point的情况下两次修改i的值。这是undefined behavior

答案 1 :(得分:6)

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

i = ++i % 3;

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

您希望i拥有什么价值?如果您使用i将全新值分配到i = ...,那么您尝试使用++i会产生什么影响?如果这是parallel-universe-C,其中这样的代码实际上有意义,那么 - 在最好的情况下 - 递增的i会立即替换为分配的全新值。那么为什么不把它写成

i = (i+1) % 3;

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

答案 2 :(得分:4)

正如其他人所指出的,行为是undefined

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#和C#不同) Java,确实做出了这些保证)。例如,表达式++i具有结果i + 1)和副作用(增加{{1}中存储的值});但是,可以推迟副作用,直到评估出更大的表达。 IOW,允许以下一系列操作:

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

Oopsie。不是我们想要的。

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