2.6 C语言的运算符和表达式

2.6.1 C语言运算符和表达式简介

C语言表达式是用运算符和括号将运算对象(常量、变量和函数等)连接起来的、符合C语言语法规则的式子。运算符(即操作符)是对运算对象(又称操作数)进行某种操作的符号。

1.C语言运算符

C语言为了加强对数据的表达、处理和操作能力,提供了大量的运算符,它们可以与运算对象一起组成丰富的表达式。例如:

1+2*3-10

其中,“1”“2”“3”“10”称为运算对象,“+”“*”“-”称为运算符。

上面的表达式先进行*运算,再进行+运算和-运算,这是因为运算符的优先级不同,*的优先级高于+和-,所以先进行*运算。进行-运算时,是7减10,而不是10减7,这是由运算符的结合性决定的,-运算符的结合性是从左到右。

由此可知,运算符不仅具有不同的优先级,还有不同的结合性。在表达式中,各运算量参与运算的先后顺序不仅要遵守运算符优先级别的规定,还要受运算符结合性的制约,以便确定是自左向右进行运算还是自右向左进行运算。运算对象先与左边的运算符结合的称为具有左结合性,运算对象先与右边的运算符结合的称为具有右结合性。除单目运算符(即只需要一个操作数的运算符)、条件运算符和赋值运算符是右结合性外,其他运算符都是左结合性。

C语言的运算符可分为以下几类,见表2-4。运算符的优先级和结合性详细情况见附录B运算符的优先级和结合性。

表2-4 C运算符分类

续表

2.C语言表达式

用运算符和括号将运算对象(常量、变量和函数等)连接起来的、符合C语言语法规则的式子称为表达式。单个常量、变量或函数可以看作是表达式的一种特例。将单个常量、变量或函数构成的表达式称为简单表达式,其他表达式称为复杂表达式。

C语言包含以下几类表达式:

(1)算术表达式,如a+b*c;

(2)关系表达式,如x>y;

(3)逻辑表达式,如x>=0&&y>>0;

(4)赋值表达式,如a=5;

(5)逗号表达式,如x=3,y=6。

本章主要介绍算术表达式、赋值表达式以及逗号表达式,关系表达式、逻辑表达式一般作为条件来使用,因此在选择结构章节中进行介绍。

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

算术运算符分为单目运算符和双目运算符两类,单目算术运算符只需要一个操作数,操作数一般位于运算符的右边(自增、自减运算中也可位于运算符的左边),双目算术运算符需要两个操作数,操作数位于运算符的两边。

1.5种基本算术运算符

C语言中的算术运算符可以用来进行各种数值计算,基本的算术运算符包括以下5种:

+(加法)、-(减法/取负)、*(乘法)、/(除法)、%(求余数)

这5种算术运算符的运算规则与代数运算规则基本相同,但也存在一些不同之处。算术运算符的运算规则可以通过表2-5进行说明。

表2-5 C算术运算符

对于表2-5有两点需要加以说明。

(1)除法运算(/)

两个整数相除,其商为整数,小数部分被舍弃,例如,5/2 = 2;若两个运算对象中至少有一个是浮点型,则运算结果为浮点型,例如,5.0/2 = 2.5。

(2)求余数运算(%)

要求运算符两侧的操作数均为整型数据,结果是整除后的余数。运算结果的符号随不同系统而定,在Turbo C中,运算结果的符号与被除数相同。例如,7%3、7%-3的结果均为1;-7%3、-7%-3的结果均为-1。如果x%y中,x能被y整除,则结果为零。求余运算符不能应用到float型或double型。

2.自增、自减运算符

自增、自减运算符均为单目运算符,它们的作用是分别使单个变量的值增1或减1。自增、自减运算符都有前置运算和后置运算两种用法。这两种运算符的运算对象可以是整型、浮点型或字符型变量,但不能是常量。从运算结果看i++相当于i=i+1,利用自增运算符的写法使得程序更加简洁。

(1)前置运算。运算符放在变量之前,以整型变量i为例:

++i、--i

前置运算可以分成两种情况来分析。

① 当运算中只包含前置运算,仅仅只需要对该对象执行增 1 或减 1 操作,不存在先后问题。

例如,++i;或--i;

相当于 i=i+1;或i=i-1;

② 当运算中包括前置运算和其他运算时,则需要先使变量的值增(或减)1,然后再以变化后的值参与其他运算,即先增(减)、后运算。

例如,如果i的原值等于3,则执行下面的赋值语句后,

j = ++i;

i的值先增1变成4,再赋给整型变量j,j的值为4。

(2)后置运算。运算符放在变量之后,仍以整型变量i为例。

i++、i-

同理,后置运算也可以分成两种情况来分析。

① 当运算中只包含后置运算,仅仅只需要对该对象执行增1或减1操作,不存在先后问题,等同于前置运算的处理。

例如    i++;或i--;

相当于   i=i+1;或i=i-1;

② 当运算中包括后置运算和其他运算时,则需要变量先参与其他运算,然后再使变量的值增(或减)1,即先运算、后增(减)。

例如,如果i的原值等于3,则执行下面的赋值语句后,结果不同。

j = i++;

先将i的值3赋给j,j的值为3,然后i增1变成4。

【例2.12】 自增、自减运算符的用法与运算规则。

#include <stdio.h>

void main()

{

  int x = 6, y;

  printf("x = %d\n", x);  // 先输出 x 的初值

  y = ++x;         // 前置运算:x 先增1(=7),然后再赋值给y(=7)

  printf("y = ++x : x = %d,y = %d \n", x , y);

  y = x -- ;        // 后置运算:先将 x 的值(=7)赋值给y(=7),然后 x 再减1(=6)

  printf("y = x--:x = %d,y = %d\n", x , y);

}

关于自增、自减运算符说明如下。

(1)自增、自减运算符不能用于常量和表达式。例如,5++、--(a+b)都是非法的。

(2)在表达式中,连续使用同一变量进行自增或自减运算时,很易出错,所以最好避免这种用法。

例如,表达式( x++)+ (x++)+ (x++)的值等于多少(假设x的初值为3)?在Turbo C系统下,该表达式的值等于 9,变量x的值变为6。因为x自增了3次,x自身的值为6。

(3)使用++和--时,在书写时最好采用大家都能理解的写法,避免误解。例如,不要写成i + + + j的形式,这会产生二义性,最好写成 ( i + + )+ j或 i +( + + j)的形式。

(4)在printf()函数中,打印的各项目的求值顺序随各系统而定,在Turbo C系统中是从右向左。例如,在如下程序段中,设i的初值为5,则

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

的输出结果为:6,5

3.算术表达式

用算术运算符和括号将运算对象(常量、变量和函数等)连接起来的、符合C语言语法规则的式子,称为算术表达式。例如,a+b*c-2+'b'都是合法的算术表达式,表达式的结果为一个算术值。运算符都有优先级和结合性,因此在求表达式的值时,要按照优先级的高低以此计算;如果在一个运算量两侧运算符优先级相同,则按规定的结合方向进行。

算术运算符的优先级与代数中相同,即先乘除取余,后加减,结合性是从左到右,如当表达式中有多个加法或减法时,则按从左到右的顺序求值。

2.6.3 赋值运算符和赋值表达式

1.赋值运算

C语言中符号“=”就是赋值运算符,从形式上看,它和数学中谈到的等号相同,但是两者的含义却存在着区别。数学中的“=”表示相等关系,如x=y表示x和y相等;C语言中“=”表示赋值关系,它的作用是将一个表达式的值赋给一个变量。由“=”连接的式子称为赋值表达式,其一般形式为

变量=表达式

(1)赋值表达式的计算

赋值表达式的计算过程可以通过以下步骤进行描述:

① 计算赋值运算符右侧“表达式”的值;

② 将计算结果赋给左侧的变量;

③ 赋值表达式的值就是左侧被赋值变量的值。

例如:a=8+5

该表达式是一个赋值表达式,执行过程如下:

① 先运算赋值运算符右侧的表达式,如上式是加法运算8+5,结果为13;

② 将结果13赋值给赋值运算符左侧的变量a;

③ 整个表达式的值即为被赋值变量a的值,即表达式a=8+5的值为13。

注意:

① 赋值运算符左侧的标识符称为“左值”,并不是任何对象都可以为左值,左值必须是一个有存储空间的量,因此变量可以为左值,表达式不能为左值,例如b+c不能为左值。常量不能被修改,也不能被赋值,所以常量也不能为左值。与之相对应的赋值运算符右侧的称为“右值”。

② 当表达式值的类型与被赋值变量的类型一致时,可以直接赋值;当表达式值的类型与被赋值变量的类型不一致,但都是数值型或字符型时,系统会自动地将表达式的值转换成被赋值变量的数据类型,然后再赋值给变量,这一点将在类型转换一节中专门介绍。以y是整型变量为例:

y = 5.0/2        // 将表达式的值(=2.5)赋值给变量 y

(2)赋值运算符的结合性

赋值运算符具有右结合性,因此在表达式中若出现多个赋值运算符应遵循从右到左的顺序进行计算。例如:

a=b=c=5可理解为a=(b=(c=5))。

① 先计算表达式c=5,得到值为5,即转换为a=b=5;

② 再计算表达式b=5,得到值为5,即转换为a=5;

③ 表达式a=5的值为5,因此整个表达式a=b=c=5的值为5。

(3)赋值运算符的优先级

凡是表达式可以出现的地方均可出现赋值表达式,因此涉及其他运算符的混合运算。从书后附录中的运算符和结合性可以看出,赋值运算符优先级低于算术运算符。例如:

x=(a=5)+(b=8)

这个表达式是合法的,对于该表达式的计算还需要考虑运算符“()”的优先级,由于“()”高于“+”,“+”的优先级高于“=”,因此先计算赋值表达式a=5和b=8的值,得到5和8,再计算两者的和赋予x,故x应等于13。

2.复合的赋值运算

在赋值符“=”之前加上其他双目运算符可构成复合赋值符,如+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=。构成复合赋值表达式的一般形式为:

  变量 双目运算符=表达式

等价于

  变量=变量 运算符 表达式

例如:

i += 3      // 等价于i = i + 3

注意:当赋值运算符右侧的表达式非单个常量、变量或函数时,等价形式中表达式外需要加一对圆括号,否则可能出错。

y *= x + 6   // 等价于 y = y * (x + 6),而不是y = y * x + 6

复合的赋值运算符也是赋值运算符,因此优先级和结合性与“=”相同。例如,整型变量a的初值为6,求赋值表达式“a+=a-=a*=a”的求解步骤如下。

① 先计算表达式“a*=a”,它相当于a=a*a,则a=36,得该表达式的值为36,即原表达式转换为“a+=a-=36”。

② 再计算“a-=36”,它相当于a=a-36,则a=0,得该表达式的值为0,即原表达式转换为“a+=0”。

③ 最后计算“a+=0”,它相当于a=a+0,则a=0,即原表达式的值为0。

2.6.4 逗号运算符和逗号表达式

在C语言中逗号“,”也是一种运算符,称为逗号运算符。其功能是把两个表达式连接起来组成一个表达式,称为逗号表达式。其一般形式为:

表达式1, 表达式2

其求值过程是分别求两个表达式的值,并以表达式2的值作为整个逗号表达式的值。

例如: 2+3, 4+5

先计算表达式1的值为5,再计算表达式2的值为9,则该逗号表达式的值为9。

逗号表达式的一般形式可以扩展为:

表达式1,表达式2,……,表达式N

从附录B中可知,逗号运算符的优先级是最低的,它的结合性是从左到右。因此计算各表达式的值后,最后表达式N的值即为整个逗号表达式的值。

【例2.13】 逗号运算符的运用。

#include <stdio.h>

void main()

{

  int a=2,b=4,c=6,x,y;

  y=(x=a+b,b+c);

  printf("y=%d, x=%d\n",y,x);

}

程序分析:由附录B中可得几种运算符的优先级“()”>“+”>“=”>“,”(>表示“高于”),即计算表达式y=(x=a+b,b+c)时,按以下步骤进行:

(1)先计算括号里的逗号表达式“x=a+b,b+c”,即逗号表达式“x=6,10”;

(2)再计算表达式“x=6”,得x的值为6,该表达式的值为6,进而(1)中的逗号表达式转换为表达式“6,10”,可得值为10;

(3)再计算表达式“y=10”,可得 y 的值为 10。y 等于整个逗号表达式的值,也就是表达式2的值,x是第一个表达式的值。

对于逗号表达式还要说明以下三点。

(1)逗号表达式一般形式中的表达式1和表达式2 也可以又是逗号表达式。例如:

(x=2*3,x+2),x+5

先计算x的值为6,再计算x+2得8(x的值仍为6),最后计算x+5得11,即整个逗号表达式的值为11。

(2)程序中使用逗号表达式,通常是要分别求逗号表达式内各表达式的值,并不一定要用整个逗号表达式的值,常用于循环结构中。

(3)不是在所有出现逗号的地方都组成逗号表达式。例如:

int a,b;

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

上例在变量说明、函数输出项列表中逗号只是用作各变量之间的间隔符。