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

vv1*_*133 5 c

int main(void)
{
    int i = 0;
    i = ++i % 3;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我这样编译:

$ gcc -Wall main.c -o main
main.c: In function ‘main’:
main.c:4: warning: operation on ‘i’ may be undefined
Run Code Online (Sandbox Code Playgroud)

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

Fre*_*son 11

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


Joh*_*all 6

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

i = ++i % 3;
Run Code Online (Sandbox Code Playgroud)

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

你想i拥有什么价值?如果你要指定一个全新的价值为ii = ...,什么样的影响是你想实现与++i?如果这是parallel-universe-C,其中这样的代码实际上有意义,那么 - 在最好的情况下 - 增量i会立即被分配的全新值替换.那么为什么不把它写成

i = (i+1) % 3;
Run Code Online (Sandbox Code Playgroud)

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


Joh*_*ode 4

正如其他人指出的那样,该行为是未定义的

6.5 表达式
...
2 在上一个和下一个序列点之间,对象的存储值最多应通过表达式的求值修改一次。72)此外,应只读先前值以确定要存储的值。73)
...
72) 浮点状态标志不是对象,可以在表达式中多次设置。73) 本段呈现未定义的语句表达式,例如
    我=++i+1;
    a[i++] = i;
同时允许
    我=我+1;
    a[i] = i;

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

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

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

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

哎呀。不是我们想要的。

这是一个深思熟虑的设计决定;这个想法是,它允许编译器以最佳方式重新排序计算(例如,通过利用寄存器中已有的值)。缺点是某些表达式组合会产生不可预测的结果。