第 10 讲:运算符(二):自增自减运算符
之前我们说到了基本的算术运算符,今天我们来说一下自增自减运算符。自增自减运算符是专门针对于一个变量本身进行增大和减小操作的运算符。
Part 1 前缀和后缀运算
在这之前,我们先说一个概念。前缀运算符(Prefix Operator)是指,运算符放在变量的左边的行为;后缀运算符(Suffix Operator)则相反:放在变量的右侧的行为。
C# 里的自增运算符写成 ++
、自减运算符写成 --
。这两个运算符既可充当前缀运算符,也可以充当后缀运算符。用法是这样的:
比如这个例子里,a++
里的 ++
放在变量后,所以称为后缀运算符;++a
的 ++
和之前的算术运算符不同,算术运算符并不能像这样单独使用,而必须依赖于赋值行为。因为算术运算符将两个数值关联起来,进行运算;然后得到的结果还必须得赋值给另外一个变量,否则这个数值结果就“找不到”了;而自增自减运算符并不一样,它可以单独使用。下面我们来挨个进行说明。
Part 2 前缀自增自减运算
前缀自增运算和前缀自减运算的行为类似,因此放在一起说明。
首先,a
是没有赋值的状态,而 b
初始数值是 4。第二句话,a = ++b
表示,将 ++b
的结果赋值给 a
。
下面说一下 ++
和 --
++
:将紧挨着的这个变量先自增一个单位大小,一般用于整数数值(小数也可以自增一个单位,不过一般用不上);--
:将紧挨着的这个变量先自减一个单位大小(同上,小数也可以用,但一般用于整数)。
如果把 ++
作为前缀运算,那么自增运算过程必须先执行;当自增完成后,自增完毕后的结果会提供给语句的别处使用;--
是同理的,只是把自增操作改成自减操作。
这句话意思就是,a = ++b
里会用到 b
的结果的地方除了 ++
(自增)以外,还有 =
(赋值)。按照逻辑,由于是前缀运算,因此变量 b
会因为优先执行自增运算的关系,将原始的数值 4 变为 5。接着,++b
整体的结果也是这个 5,于是将 5 赋值给变量 a
。整体就是这个逻辑。所以,前缀自增运算符包含两个语义:
将变量本身自增一个单位;
表达式整体也和算术运算符一样,有一个结果。这个结果就是变量增大后的结果。
同理,--
一样的。
b
会变为 3,然后得到 --b
整体的结果也是 3。最后把 3 赋值给 a
,完成操作。
Part 3 后缀自增自减运算
麻烦的事情来了。前缀运算看起来运算很简单,不过后缀运算就麻烦了。++
和 --
是可以作为后缀运算出现的,因此更不好理解的是这里。
++
:优先将变量本身的数值丢给别处使用;当当前语句的别处全部用完这个变量的数值后,变量再增大一个单位;--
:同理,只是改成减去一个单位。
我们看一下这个例子。
这个例子里,a = b++
里使用 b
的地方有两处:=
(赋值)和 ++
(自增)。由于是后缀运算,因此变量 b
b
变量”的地方了。显然,除了自增以外,只有赋值在用 b
。因此,a = b++
等效于直接先赋值:a = b
,然后 b
增大一个单位。因此,a
在获取数值的时候,还是 b
原本的数值 4,而不是 5;但 a = b++
执行完毕后,b
会自增一个单位,从 4 变为 5。
道理也不难。将 a = b++
改成 a = b--
是一样的道理,只是把自增改成自减。因此 a
结果依旧是 4,只是 b
从原本的数值 4 变成 3。
Part 4 将自增自减运算单独作为语句使用
前文里我们还说到了一种用法:a++;
,即单独把自增自减运算符当成语句来用。这样的操作和前文的前缀和后缀操作是一样的,只是特殊之处在于,“别处使用变量”的行为就没有了:整个语句唯一的操作就是在自增(或自减)变量数值。
如果单独作为语句使用,++a
和 a++
完全没有区别(行为上就是单纯为了变量本身能增大或减小一个单位)。同理,--a
和 a--
也是一样。
可能你学过一些汇编语言,C 语言里
++a
和a++
作为语句的时候,在汇编执行上有一点点不同(所以有些人会告诉你优化性能的时候,请优先使用前缀的自增自减运算,而不是后缀的;但是实际上,通过编译器优化,它们都会变成完全一样的东西。这一点在 C# 里,不论是前缀还是后缀,都是完全一样的:只要++a
和a++
作为语句单独出现的话,不论在哪里,它们都是一样的东西。请务必记住这一点。虽然考试不考,但是一定不要在这种地方上钻牛角尖。
另外,a++;
完全等价于这么一句话:a = a + 1;
。这句话未免有些奇怪,在初学 C 语言的时候,我们就对这个左右完全不相等的表达式头疼。实际上,这个等号是赋值行为,因此实际上就是把 a
的数值增大一个单位之后,得到的 a + 1
的结果,再重新赋值给 a
自身的过程;同理,a--;
等价于 a = a - 1;
。
Part 5 混用自增自减运算
5-1 复杂用例
有些时候,为了简化代码的书写,我们可能会使用自增自减运算符。但是,有可能多个自增自减运算符会同时出现在一个语句里。
思考一下,这个例子的输出结果是多少。答案是 8,因为有一个自增运算符是作后缀的,所以变量会先提供别处使用。整个表达式里,使用 a
+
和 ++
两处。那么“别处”自然就指的是这里的 +
了。即使我们打了括号,但是因为出现在变量的后面,所以 ++
也不会提前执行。这一点在初学是非常让人头疼的。
接着,a
既然提供使用,那么原始数值是 3。
然后,++b
的 ++
是前缀运算,因此会先将 b
增大一个单位后,再提供给别处使用。b
从 4 改成 5。b
自增完成后,提供给别处使用(这里还是只有 +
在用 b
)。综合前面的 a
,我们可以发现,实际上 c
赋值的时候,a + b
的结果其实是 3 + 5
而不是别的,因此 c
的结果是 8。
不过,当我们走到第 3 行代码的时候,a
和 b
变量已经通过原始的自增操作完成了增大一个单位的过程,因此 a
是 4、b
是 5,c
是 8,也因此,输出结果是 4, 5, 8
。
混用自增自减运算有时候是可以简化代码,但有时候会复杂化代码的理解逻辑,因此,谨慎使用类似前面这个例子这样的混用模式。
5-2 千万别混用同一个变量的自增自减
像是前面这个例子逻辑还算清晰。如果这样呢:
我估计专业学过 C# 的朋友也没几个看得懂这个例子的计算,以及结果输出。想听我告诉你答案吗?
从人类的理解角度来说,我们肯定会优先计算 ++a
。但自增后的 a
会不会影响到左边的 a++
的 a
呢?按道理是会的,毕竟是同一个 a
;然后得到 b
的结果 4 + 4(前面这个是后缀运算,因此先用于加法运算,之后才自增)。所以答案是 a = 5、b = 8。
但是!你真的觉得这个是人应该写出来的程序吗?这种反人类的例子还是不要出现在你的代码里。
VS 的结果就是 5 和 8。
呃……我刚还说不告诉你结果,结果这里还推了一遍结果,还讲了一遍……
Part 6 总结
这一节的内容我们学到了自增和自减运算。自增自减运算都有如下两个语义行为:
把变量的增大(或减小)一个单位大小;
将变量的结果当作自增自减运算符这个表达式的数值,提供给当前语句的别处使用。
但我们说过,混用会引起代码逻辑的混乱,因此不建议混用自增自减运算。最不建议的就是同变量的自增自减。