1
C语言程序设计
1.3.4.1 2.4.1 算术运算符和算术表达式

2.4.1 算术运算符和算术表达式

1.基本运算符

在C语言中,基本的算术运算符有5个,它们是:

  +(加法运算符,或正值运算符,如13+50、+50);

  −(减法运算符,或负值运算符,如50−32、−32);

  * (乘法运算符,如13*8);

  / (除法运算符,如23/4);

  %(取模运算符,或称求余运算符)。

在使用时应注意以下几点:

(1)两个整数相除,结果仍为整数,商向下取整。实际上是整除运算。如20/3的结果为6,5/6的结果为0。但是,如果除数或被除数中有一个为负值,则舍入的方向是不固定的。例如,−5/3,有的机器上得到结果−1,有的机器得到结果−2。多数机器采取“向零取整”的方法,即−5/3=−1,取整后向零靠拢。

如果参加+、−、*、/运算的两个数中有一个数为浮点数,则结果是double型。因为自动转换后所有数都按double型进行运算。关于数据类型的转换将在2.5节中详细介绍。

(2)取模运算符%实际上是数学运算中的求余运算符,其两个操作对象都必须是整数。结果的符号与%左边的操作数的符号相同。如20%6的结果为2,−45%8的结果为−5,45%−8的结果为5。

(3)减法运算符还可以作为取负运算。如上面的45%−8中的“−”号就是取负运算。这时它是一个单目运算符。除此之外,其他的运算符在使用时,都需要两个操作数(或运算分量),如a+b、i*j等,所以它们都是双目运算符。

2.算术表达式和运算符的优先级与结合性

算术表达式就是用算术运算符和圆括号将操作数(或运算对象)连接起来的、符合C语法规则的式子。操作数(或运算对象)包括常量、变量、函数等。算术表达式的解就是经过算术运算得到的表达式的值。例如,下面是一个合法的C算术表达式:

  i*j/k−20.9+'d'

上面的表达式中有四个运算符,即:*、/、−和+,怎么进行运算呢?这就涉及运算符优先级的问题。

C语言规定了运算符的优先级和结合性。在表达式求值时,先按运算符的优先级高低次序执行。在C语言中,每个运算符都有一个与之相关的优先级别。如果不同级别的多个运算符同时出现在一个表达式中,那么,具有较高优先级的运算符首先得到计算。即:按优先级从高到低的顺序依次执行。对于这5个基本算术运算符来说,它们的优先级次序如图2.4.1所示。

img100

图2.4.1 优先级次序

例如,表达式−i−j*k,i的左侧为“−”号,表示的是取负运算,而不是减号。因为“−”号前没有运算对象,只有其后有一个运算对象,所以“−”号在这里应是单目运算符,为取负运算。根据上面讲的优先级顺序,“−”(取负运算)优先级最高,因此应先对i取负,j左侧的“−”号应为减法运算符,因为这里的“−”号的左右侧都有运算对象,是一个双目运算符,而j的右侧为“*”号,而乘号优先于减号,因此,相当于(−i)−(j*k)。

如果在一个运算对象两侧的运算符的优先级别相同,如m−n+a,则按规定的“结合方向”处理。

C规定了各种运算符的结合方向(结合性:是指同一优先级运算符的运算先后次序),算术运算符的结合方向为“自左至右”,因此n先与减号结合,执行m−n的运算,再执行与a的运算。“自左至右的结合方向”又称为“左结合性”,即运算对象先与左面的运算符结合。

如果一个运算符两侧的数据类型不同,则会按2.5节所述,先自动进行类型转换,使二者具有同一种类型,然后进行运算。

3.自增、自减运算符

C语言中提供了两个特殊的运算符:自增运算符“++”和自减运算符“−−”。其作用是使变量的值增1或减1,它们都是单目运算符,可以出现在运算分量的前面或后面。当出现在运算分量的前面时,如++i,−−i,称之为前缀运算符;当出现在运算分量的后面时,如i++,i−−,称之为后缀运算符。

表达式++i和i++的作用都相当于i=i+1,表示将i的内容在原来的基础上加1。

表达式−−i和i−−的作用都相当于i=i−1,表示将i的内容在原来的基础上减1。

++i和−−i是前缀表示法,i++和i−−是后缀表示法。如果直接在++i和i++的后面加上分号构成C的执行语句,即"++i";和"i++";,前缀和后缀并无区别,都是使i的值在原来基础上加1(−−符号也一样)。但是,将它们用在表达式中,前缀和后缀则是有区别的。

前缀表示法是先将i的值加1/减1,再在表达式中使用;而后缀表示法是先在表达式中使用i的当前值,再将i的值加1/减1。如果i的原值等于51,则执行下面的赋值语句:

(1)j=++i;  (i的值先加1变成52,再赋给j,j的值为52)

(2)j=i++;  (先将的i值赋给j,j的值为51,然后i再加1变为52)

使用自增运算符和自减运算符时,需注意以下几点:

(1)自增运算符(++)和自减运算符(−−),只能用于变量,而不能用于常量或表达式。如55++或(i+j)++都是不合法的。

(2)++和−−是单目运算符,其优先级高于基本的算术运算符,与单目运算符−(取负)的优先级相同。其结合方向是“自右至左”。前面已提到,算术运算符的结合方向为“自左至右”。如果有表达式−j++,j的初值为100,则该表达式应如何计算呢?j的左边是负号运算符,右边是自加运算符。若按左结合性,相当于(−j)++,而(−j)++是不合法的,因为对表达式是不能进行自加/自减运算的。所以,应按右结合性,为−(j++)。若printf("%d",−j++),则先取出j的值100,输出−j的值−100,然后j增值为101。

(3)尽量不要在一般的表达式中将自增(减)运算符与其他的运算符混合使用。

4.有关表达式使用中的问题说明

(1)ANSI C没有具体规定表达式中的子表达式的求值顺序,允许各编译系统自己安排。

如果a的初值为6,有以下表达式:

(a++)+(a++)+(a++)

该表达式的值是多少呢?有的系统按照自左至右顺序求解括弧内的运算,求完第1个括弧的值后,实现a的自加,a值变为7,再求第2个括弧的值,结果表达式相当于6+7+8,即21。而一些系统(如Turbo C和MS C)把6作为表达式中所有a的值,因此3个a相加,得到表达式的值为18。在求出整个表达式的值后实现a自加3次,a的值变为9。

应该避免出现这种歧义性。如果编程者的意愿是想得到21,可以写成下列语句:

  a=6;

  i=a++;

  j=a++;

  k=a++;

  d=i+j+k;

执行完上述语句后,d的值为21,a的值为9。虽然语句多了,但不会引起歧义,无论程序移植到哪一种C编译系统运行,结果都一样。

(2)C语言中有的运算符为一个字符,有的运算符由两个字符组成,在表达式中如何组合呢?C编译系统在处理时尽可能多地自左至右将若干个字符组成一个运算符(在处理标识符、关键字时也按同一原则处理),如a+++b,系统理解为(a++)+b。

(3)在调用函数时,多数系统对函数参数的求值顺序是自右至左。如a的初值为90,则下面的函数调用:

  printf("%d,%d",a,a++);

输出的是“91,90”。printf函数输出两个表达式的值(a和a++分别是两个表达式),先求出第2个表达式a++的值90(a未自加时的值),再求第1个表达式的值,由于在求解第2个表达式a++后,使a加1变为91,因此printf函数中第一个参数a的值为91。而有的系统按自左至右求值,输出“90,90”。因此,以上这种写法不宜提倡,最好改写成:

  b=a++;

  printf("%d,%d",b,a);