C之旅(一)运算符——优先级,结合性和求值顺序

时间:2021-06-01 17:22:54

本节主要讲基本运算符的优先级,结合性和求值顺序。
先看一个表达式- (1 + 2) * 3 + (4 + 5 * (6 + 7 ))
记住你对它的运算过程,看完下面的内容之后,也许你会用不同的方式来看待这个表达式。

基本运算符 = + - * /

C中没有指数运算
运算符操作的是操作数,操作数就是放在运算符两侧的东西。

(1) 赋值运算符 =

year = 2016; 读作将值2016赋给year,而非year等于2016。
=将2016赋给变量year,是从右到左的,即=具有右结合性。
2016 = year; 这种写法是错误的,不能给常量赋值。
那么,=的左边应该放什么样的对象?
答案是必须是一个存储位置。year是一个变量名,代表的是一个地址,这里可以存储值。
year可以被赋值,我们称这类可以被赋值的实体为可修改的左值
右值指可以赋给可修改的左值的量,当然,右值就不必在乎可不可以修改了。

=运算符的操作数有哪些呢?

(2)加法运算符 +

+将两侧的值加到一起。
+的操作数可以是变量也可以是常量。

(3)减法运算符 –

-从它前面的数减去它后面的数。
-的操作数可以是变量也可以是常量。

(4)符号运算符 + -

先看一个表达式:
-(1990 - 2016)
第一个减号-和第二个减号-含义相同吗?
答案是不相同。
+ - 作用于两个操作数时称为二元算数运算符,当它们作用于一个操作数时,称为一元符号运算符
-作用于一个操作数时,用于指示或改变其代数符号。
一元运算符+在C90标准才加进C中,这之前是不允许+1990这种语句的。一元运算符+不改变它的操作数的值或符号。

(5)乘法运算符 *

*将左右两个操作数相乘。
*的操作数可以是变量也可以是常量。

(6)除法运算符 /

在讲前面的运算符时,为什么一直强它们调操作数呢?因为有些运算符不能作用于某些类型,也有些运算符作用于不同的操作数结果不同
/左边的值被右边的值除。
/作用于浮点数,结果为浮点数,如2.0/3.0的结果为0.666667(为什么不是0.666666?)
/作用于整型数,结果为整数,如2/3的结果为0,而不是0.6或0.7,因为自从C99之后整数除法采用了趋零截尾的方式,直接截掉小数点后的数字。
2.0/3.0, 2/3结果都可知了,那么2.0/32/3.0的结果是什么?
先有一个这样的意识,如果算术运算符两侧操作数的类型不同,会引起类型转换
2.0/3或2/3.0中,首先将整型值提升为浮点型,然后再做除法运算,结果为0.666667(为什么不是0.666666?)

 

既然是趋零结尾,那么C中怎么实现四舍五入呢?

可以巧妙的利用趋零结尾的方式,对正数a用(int)(a+0.5)方式,对负数a用(int)(a-0.5)的方式。

 

(7)运算符的优先级,优先级和求值顺序

下面表达式的结果是什么?运算顺序是怎样的?
- (1 + 2) * 3 + (4 + 5 * (6 + 7 ))

按照我们正常计算的思维,会先算2*3,第一个加号之前的结果为-6,然后再算后面括号里表达式的值。

 

在计算上面的表达式之前,我们先来看一个简单的表达式,来解释一些概念。
2*3+4*5
想一下,在你刚刚计算的时候,什么时候用到了优先级?
当两个运算符共享同一个操作数的时,优先级决定了求值顺序。2*3+4*5中,4为+和*共享,优先级就规定先对4进行乘法运算,这保证两个*运算在+运算之前进行,在2*3+4*5这个表达式中,优先级的作用到此为止了。

接着考虑,2*3和4*5先计算哪个?
你可能会认为是先计算2*3,理由是加法运算符+的结合性。+作为二元运算符时,结合性是从左到右,所以先计算+的左边,再计算右边。如果你这样想,就对结合性理解有误了。


什么是运算符的结合性?
我们看表达式2*3+4*5-6*7,乘法运算过后,表达式简化为6 + 20 - 42,这时应该先计算6+20,再计算26-42,因为有相同优先级的+和-共享同一操作数20,所以+发挥了结合性的作用。记住,结合性适合于共享同一操作数的运算符(注意与优先级的作用进行区分)。在2*3+4*5中,两个*不共享同一操作数,而且先进行乘法运算,简化为6+20之后,没有操作数是共享的。所以上边的想法就有误了。


先计算哪个乘法?
2*3+4*5中,到底先进行哪个乘法运算呢?答案是不确定,看具体的实现,由设计者决定。特别需要注意的是,优先级只保证乘法在加法前进行,并不确定两个乘法哪个先进行。

 

了解了上面的概念,现在我们来看表达式
- (1 + 2) * 3 + (4 + 5 * (6 + 7 ))
根据优先级,先计算圆括号里的表达式,这里面有三个圆括号(),先计算哪个呢?
可以确定的是红色括号里的表达式在绿色括号里进行,但蓝色括号和绿色括号之间谁先被运算,依赖于实现。
注意,前半部分(1+2)运算完之后是-3*3,然后第一个3和它前面的一元符号运算符结合为-3,然后再乘以3。