自增减运算符
自增、自减表达式及其运算符可以算是 C/C++ 里最难的运算符。不过不用太在意,我们并不会特别深地讲解它们,在实际的使用中也用不上过深的知识。
先简单认识一下这两个运算符:
操作符 | 名称 | 作用 |
---|---|---|
++a 或 a++ | 前/后缀自增运算符 | 使操作数 += 1 |
--a 或 a-- | 前/后缀自减运算符 | 使操作数 -= 1 |
你应该发现了这里的操作符分为前缀和后缀两类。我们将分别简单地介绍它们——你可能觉得它们之间几乎没有什么区别,不过不必担心,我们稍后会做细致地区分。
前缀自增/减表达式
形如:
++操作数 --操作数
的表达式叫做前缀自增/减表达式。它的作用也正如上所说,使得操作数给自己增加1或者给自己减去 1
,完全等价执行了 += 1
或 -= 1
。因此,这个操作数必须指明一个变量*。否则,例如 ++42
这种写法是错误的。 前缀自增/减表达式的值为进行 +1
或 -1
之后的操作数本身。
同样地,自增/减运算符的操作数必须是左值。这些操作数不仅可以是算术类型,还可以是指针类型。参阅指针章节。
后缀自增/减表达式
形如:
操作数++ 操作数--
的表达式叫做后缀自增/减表达式。它的作用与前缀大致相同,使得操作数给自己增加1或者给自己减去1,大致相当于执行了一次 += 1
或 -= 1
。同样地,这个操作数也必须指明一个变量,例如 42++
这种写法也不正确。 后缀自增/减表达式的值为进行 +1
或 -1
之前的操作数的值。
分析
你足够细心的话就会观察到最明显的区别就是这两种表达式的值是不同的。前缀表达式将运算之后的操作数作为表达式的返回值;而后缀表达式将运算之前的操作数的值作为表达式的返回值。这具体又是什么意思呢?看下面这个例子:
它的运行结果是:
43 43
42 43
我们来一步一步分析。首先 x1
和 x2
都赋予了 42
这个值。然后下面:
- 第一步:令
x1++
,然后把这个表达式赋给y1
。x1++
,让x1
自增1
,现在x1
等于43
。然后正如刚才所说,后缀表达式将运算前的操作数的值作为表达式的值,因而此时x1++
这个表达式的值为运算之前x1
的值,就是42
。所以整句话相当于把42
赋值给y1
。 - 第二步:令
++x2
,然后把这个表达式赋给y2
。++x2
,让x2
自增1
,现在x2
等于43
。然后正如刚才所说,前缀表达式将运算后的操作数作为表达式的值,因而此时++x2
这个表达式的值是运算之后的x2
,也就是43
。所以整句话相当于把43
赋给y2
。
这样就得到了上述结果: x1
和 x2
都因为自增结果为 43
,而 y1
为 42
, y2
为 43
。如果你觉得这件事情不好记住的话,可以总结为下面两句话:
- 前缀运算符,先运算,后返回值;
- 后缀运算符,先返回值,后运算。
如果你还觉得这件事情理解起来非常困难,那么你完全可以不用管它,尽量避免使用自增/减表达式的返回值即可。千万不要纠结于这种用处不大的细节上。
未定义行为
对于某些表达式的值,在不同编译器和不同编译环境下的结果是不同的。比如:
(--x) * (x--)
这件事情,由于无法确定乘法运算符两侧的表达式求值顺序,于是无法确定到底是在计算 (x - 1) * (x - 1)
还是 (x - 2) * x
还是其它的结果。
像这种使用方法都是 C++ 标准中没有给出具体规定的,所以它的值和副作用都是不可预测的,也都是合乎 C++ 标准的。有玩笑说,当你运行这个表达式语句的时候,编译器选择自动关机都是合理的。这些没有指明编译器行为的做法称为未定义行为(Undefined Behavior, UB)。我们在写程序的时候应绝对避免未定义行为的出现。