3.1 C语言中的运算符及表达式
所谓运算符就是指运算的符号,例如加运算符(+)、乘运算符(*)、取地址运算符(&)等。表达式与运算符密不可分,它由运算符与操作数组合而成,并由运算符指定对操作数要进行的运算,一个表达式的运算结果是一个值。本节从整体上认识运算符,并学习表达式的用法。
3.1.1 运算符及其学习要点
C语言中的运算符是编译器能够识别的具有运算意义的符号。编译器把这些符号及其组成的表达式翻译成相应的机器代码,就可以由计算机运行得出正确的结果。就像日常生活当中许多东西的名字,如冰箱、电视机等分别代表不同功能的电器设备一样,运算符就是代表C语言中各个运算功能的名字,这些名字是由制定C语言规范的人员确定的。
C语言的运算符不仅具有不同的优先级,而且还有一个特点,就是它的结合性。所谓优先级是指各种运算符号的运算优先顺序。例如算术运算优先于关系运算,而算术运算中先乘除后加减等;结合性是指运算符号和运算对象的结合方向,包括从左向右(左结合)和从右向左(右结合)。其中算术运算符为左结合,例如a-b+4;而赋值运算符为右结合,例如a=b=5。在表达式中,各运算量参与运算的先后顺序不仅要遵守运算符优先级别的规定,还要受运算符结合性的制约。C语言的运算符按功能可分为以下几类。
◆ 算术运算符:用于各类数值运算,包括加(+)、减(-)、乘(*)、除(/)、求余(或称模运算,%)、自增(++)、自减(--),共7种。
◆ 关系运算符:用于比较运算,包括大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)和不等于(!=),共6种。
◆ 逻辑运算符:用于逻辑运算,包括与(&&)、或(||)、非(!),共3种。
◆ 位操作运算符:按二进制位对参与运算的量进行运算,包括位与(&)、位或(|)、位非(~)、位异或(^)、左移(<<)、右移(>>),共6种。
◆ 赋值运算符:用于赋值运算,包括简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位运算赋值(&=,|=,^=,>>=,<<=),共3类11种。
◆ 条件运算符:这是一个三目运算符,用于条件求值(? :)。
◆ 逗号运算符:用于把若干表达式组合成一个表达式(,)。
◆ 指针运算符:用于取内容(*)和取地址(&)两种运算。
◆ 求字节数运算符:用于计算数据类型所占的字节数(sizeof)。
◆ 特殊运算符:有括号()、下标[]、成员(->,.)等几种。
另外,不同的运算符需要指定的操作数的个数并不相同。根据运算符需要的操作数的个数,可将其分为3种:单目运算符(一个操作数)、双目运算符(两个操作数)和三目运算符(三个操作数)。
注意
运算符的优先级和结合性用于确定连接在一起的复杂表达式先后求值的次序,是运算符学习中比较重要的两个方面。
C语言中全部运算符的优先级和结合性如表3.1所示。
表3.1 运算符的优先级与结合性
运算符的优先级用于判定各表达式的先后执行次序。运算符的优先级越高,相关联的表达式就越先组合在一起完成求值计算。圆括号()运算符具有最高的优先级,如果圆括号是多层嵌套的,首先计算最内层配对的圆括号中的表达式。对于优先级相同的表达式序列,表达式求值的次序取决于同级运算符的结合性:左结合的运算符各表达式总体上从左到右依次求值,右结合的则相反。例如:
1-2+3 或 i-j+k
C语言规定加、减、乘、除算术运算的结合性是从左到右,于是运算的次序进一步确定为:
(1-2)+3 或 (i-j)+k
一般来讲,单目运算符和三目运算符是右结合的。双目运算符除直接赋值和复合赋值运算符是右结合的以外,其余都是左结合的。
3.1.2 表达式及其求值顺序
表达式是由运算符将运算对象连接成的式子,它描述了一个具体的求值运算过程,可由编译器解释成机器可识别的代码。表达式有两个特点,一个特点是表达式具有一定的数值,另一个特点是表达式具有类型属性。表达式的类型属性用于指出其占有的内存大小。根据表达式的形式,可将表达式分为简单表达式、基本表达式和复合表达式。
简单表达式是由一个数据,例如简单变量、常量组成,同时不含运算符的表达式。一个数据可以理解为一个表达式,但是没有运算过程。简单表达式的结果值为数据本身。类型为数据的类型。另一个值得关注的简单表达式是圆括号括起来的表达式如(e),圆括号用于提高其中表达式e的优先级。
基本表达式是只由一个运算符,以及该运算符所需的操作数(可能为1个、2个或3个)所构成的表达式。基本表达式描述了一个运算符的运算过程,运算结果值作为表达式的结果值,结果的类型即表达式的结果类型。
复合表达式指的是当一个表达式可以作为另一个运算符的操作数时,所构成的表达式就叫做复合表达式。从形式上来看,当复合表达式中含有多个运算符和多个表达式时,各个表达式的运算顺序为:
◆ 函数计算最优先
◆ 其次强制类型转换优先
◆ 自增(++)、自减(--)与取反(-)次之
◆ 先乘除,后加减
◆ 有括号先算括号内
在进行表达式求值时应注意几个问题:
◆ 表达式中各类数据在参与计算时的数据类型是否一致,若不一致则需要转换,这时候要注意默认转换的规律或者采用强制转换。具体转换方法,在后面讲述。
◆ 计算表达式时的优先级的问题,即先算什么、后算什么,在写表达式时,有时记不清楚运算符的优先级,则可使用括号来确定运算符组合。
◆ 结合性的问题,是“左结合”还是“右结合”,其决定是“从左往右算”还是“从右往左算”。特别要注意的是,在一个表达式中,可能既有“左结合”又有“右结合”存在,此时应十分小心。
注意
在表达式中,连续出现两个运算符时,最好用空格符分隔。如a+++b,最好写成a++ +b或a+ ++b的形式。
判断表达式类型应根据表达式中出现的运算符的优先级来判定。如果某一运算符在整个表达式的运算过程中优先级是最低的,或者是最后运算的运算符,那么表达式的类型就是该运算符所从属的类型。例如:
x=(a=3,b*3) /*是赋值表达式*/ x=a=3,6*a /*是逗号表达式*/ (x=8)>(y=9)+6 /*是关系表达式*/ (k=i++)/3*a /*是算术表达式*/
具体的各类型的表达式在后边的小节中说明。表达式的书写不如数学中形式灵活,要遵循下面的限制和规则:
◆ 乘号(*)不能省略。例如:x乘以y应写成x*y,不能写成xy。
◆ 括号必须成对出现,只能使用圆括号并可以出现多对圆括号。
◆ 表达式从左到右在同一基准上书写,无高低、大小区分。
例如数学上的求根公式,写成C语言的表达式为:
(-b+sqrt(4*a*c-b*b))/(2*a)
其中sqrt为求平方根的函数。初学者要学会如何熟练地将数学表达式写成C语言表达式。