3.9 认识递减和逻辑求反运算符
所谓条条大路通罗马,有时候换个思路来解决编程问题会找到更简单的方法。你看,为了从1加到N,我们所做的就是1加上2,再加上3,一直加到N,为此我们特意声明了一个变量n来实施这种数字的递增。
另一方面,从1加到N,和从N开始往1加没有什么区别,只不过方向相反。既然如此,那我们就用不着多声明一个变量,直接操作参数r更方便。以下是函数cusum的另一个版本,用的就是这种方法。
unsigned long long int cusum(unsigned long long int r) { unsigned long long int sum = 0; while(r)sum += r --; return sum; }
在这里,while语句的控制表达式仅仅是r。每次循环开始前先求值表达式r(也就是进行左值转换)以判断是否继续循环。除非变量r的存储值为0,否则整个循环过程将持续进行。
在while语句的循环体内,我们遇到了一个新的运算符--,和运算符++一样,它只需要一个操作数,而且必须是左值,但不进行左值转换。不同之处在于,运算符++使得操作数的存储值递增,而运算符--则使得操作数的存储值递减。
实际上存在两种递减运算符,即前缀递减运算符和后缀递减运算符。前缀递减运算符需要一个右操作数,例如-- r,前缀递减表达式的值,或者说前缀递减运算符的结果是其操作数递减后的值;前缀递减表达式还有一个副作用,它使得操作数的存储值在原来的基础上减一。
后缀递减运算符需要一个左操作数,例如r --,后缀递减表达式的值,或者说后缀递减运算符的结果是其操作数递减之前的原值;后缀递减表达式还有一个副作用,它使得操作数的存储值在原来的基础上减一。
前缀递减运算符的优先级和前缀递增运算符的优先级相同;后缀递减运算符和后缀递增运算符的优先级相同。
回到while语句的循环体,它是一个表达式语句,由表达式sum += r --和末尾的分号组成。如果你能理解表达式sum += n ++,你自然也能理解这个表达式。
表达式sum += r --是一个复合赋值表达式,因为运算符--的优先级高于+=。为了得到运算符+=的结果,必须先得到运算符的--结果。每次执行循环体时,都会将变量r的原值加到变量sum,变量r的存储值递减。除了求得运算符+=和--的结果,这个表达式还有修改变量sum和r的存储值的副作用。
最后一次执行循环体时,变量r的值是1。此时,表达式sum += r --是将变量r的原值,也就是1加到变量sum,然后将变量r的存储值递减。递减之后,变量r的值为0。当while语句对控制表达式r进行求值以决定是否进行下一轮循环时,因为变量r的存储值为0,所以控制表达式的值也为0,退出while语句。
对于那些非常讲究代码规范的人来说,他们坚持认为这个while语句应该写成下面这样才显得直观:
while(r ! = 0)sum += r --;
尽管我并不认为大家的抽象思维能力会如此低下,以至于非得如此画蛇添足、狗尾续貂,但我也不得不承认这样做其实也没什么毛病。在这里,“! =”是一个新的运算符,它的意思是“不等于”。注意感叹号“!”和等号“=”一定要连写,不得分开。
运算符!=属于等性运算符,等性运算符共有两个,! =是其中之一,另一个是==,意思是“等于”。等性运算符属于二元运算符,它们需要一左一右两个操作数,并共同组成等性表达式。
如果运算符!=的两个操作数在数值上不相等(“不等于”的关系成立),则等性运算符!=的结果为1,否则为0。
如果运算符==的两个操作数在数值上相等(“等于”的关系成立),则等性运算符==的结果为1,否则为0。
在这个while语句中,是先对左值r进行左值转换,然后用转换后的数值和数字0比较。如果它们的数值上不相等,则“不等于”的关系成立,控制表达式(等性表达式)的值为1,可以继续循环;否则控制表达式(等性表达式)的值为0,退出循环。
以上,控制while循环的逻辑是“r的值不等于0”。要是我们想使用运算符==,该怎么写呢?此时,控制while循环的逻辑是“r的值等于0并非事实”。如果按此逻辑,则上述while语句可以改写为
while(! r == 0)sum += r --;
这里还有一个新的运算符“!”,叫作逻辑求反运算符,它只需要一个右操作数,因此也是一元运算符。逻辑求反运算符的功能很简单:如果操作数的值为0,则运算符!的结果为1;如果操作数不为0,则运算符!的结果为0。例如,表达式!0的值是1;表达式!1的值是0;表达式!23的值是0;表达式!500500的值也是0。
运算符!的优先级低于等性运算符!=和==,在这里,运算符==的操作数是r和0;运算符!的操作数是表达式r == 0的值。因此,表达式!r == 0等价于!(r == 0)。
以上,是要先比较0和r(经左值转换后)的值,如果变量r的值不为0,则表达式r == 0的值为0(因为关系不成立),而表达式!r == 0的值为1,循环可以继续进行。否则,如果变量r的值已经递减到0,则表达式r == 0的值为1(因为关系成立),而表达式!r == 0的值为0,退出while语句。
历史上曾经发生过因为粗心大意而将r == 0写成r = 0这样的事,所以有些人强烈建议将r == 0写成0 == r,例如:
while(! 0 == r)sum += r --;
这样一来,如果将0 == r写成0 = r,这将成为一个非法的表达式而在翻译的时候出错,因为0不是左值。然而我认为,将r == 0写成0 == r说明你已经意识到这里可能会写错,所以具体怎么写已经不重要了。