这是一个非常经典且容易混淆的C语言表达式,因为它涉及到赋值运算符的结合性和副作用。

(图片来源网络,侵删)
执行 x = x = -x; 后,x 的最终值是 -10。
分步解析
要理解这个表达式,我们必须记住两个关键规则:
- 赋值运算符 () 的结合性是从右到左 (Right-to-left),这意味着在
a = b = c;这样的表达式中,它会先计算b = c,然后再计算a = (b = c)的结果。 - 赋值表达式本身会返回它所赋的值。
y = 5;这个表达式会执行赋值操作,并且整个表达式的值就是5。
我们按照这个规则来分解 x = x = -x;,并假设 x 的初始值为 10。
第1步:从最右边的表达式开始计算
由于赋值符是右结合的,编译器会这样解析表达式:
x = (x = (-x));

(图片来源网络,侵删)
我们先计算最里面的部分:x = -x。
x的值仍然是 10。-x是对x取负,结果是-10。- 现在我们执行赋值操作
x = -10,这个操作做了两件事: a. 副作用:将变量x的值修改为-10。 b. 返回值:整个赋值表达式x = -10的返回值是它刚刚赋的值,也就是 -10。
第2步:将第1步的结果代入
整个表达式变成了 x = (-10)(这里的 -10 是上一步的返回值)。
- 我们执行赋值操作
x = -10。 - 这个操作也做了两件事:
a. 副作用:将变量
x的值再次修改为-10。(虽然它已经是-10了,但这个操作还是会发生)。 b. 返回值:整个表达式x = x = -x;的最终返回值是 -10。
总结执行流程
| 步骤 | 表达式部分 | 计算过程 | x 的当前值 |
表达式的返回值 |
|---|---|---|---|---|
| 初始状态 | - | - | 10 |
- |
| 1 | x = -x |
计算 -x (即 -10)。 2. 将 -10 赋给 x。 |
-10 |
-10 |
| 2 | x = (返回值) |
将第1步的返回值 -10 赋给 x。 |
-10 |
-10 |
注意:在大多数情况下,我们只关心表达式的副作用,也就是对变量 x 的修改,最终 x 的值是 -10。
常见的误解
很多人会误以为这是从左到右计算的,即:
- 先计算
x = x,结果是x的值不变,还是10。 - 再计算
10 = -x,这显然是错误的,因为10是一个字面量(常量),不能被赋值。
这个误解源于混淆了赋值运算符和数学中的等号,在C语言中, 是赋值运算符,它的作用是将右边的值存放到左边的变量中,而不是判断两边是否相等。
代码验证
你可以运行下面的C代码来验证这个结果:
#include <stdio.h>
int main() {
int x = 10;
// 为了看清楚过程,我们分步打印
printf("初始 x = %d\n", x);
// 执行复合赋值
x = x = -x;
// 打印最终结果
printf("执行 x = x = -x; 后,x = %d\n", x);
return 0;
}
输出结果:
初始 x = 10
执行 x = x = -x; 后,x = -10
对比:x = -x;
为了让区别更明显,我们来看一下只执行一次取负赋值的情况:
#include <stdio.h>
int main() {
int x = 10;
printf("初始 x = %d\n", x);
// 只执行一次
x = -x;
printf("执行 x = -x; 后,x = %d\n", x);
return 0;
}
输出结果:
初始 x = 10
执行 x = -x; 后,x = -10
在这个简单的例子里,x = x = -x; 和 x = -x; 的最终结果看起来一样,但它们的执行过程是完全不同的,前者是一个由两个赋值操作组成的复合表达式,而后者只是一个单一的赋值操作,在更复杂的逻辑中,这种区别可能会导致不同的行为。
