C语言程序设计新编教程
上QQ阅读APP看书,第一时间看更新

2.4 常用运算符及表达式

变量在程序中主要用于存储程序输入和处理结果,而数据处理则要通过表达式运算来实现。表达式是由运算符、括号和操作对象联系起来的式子。C语言中运算符和表达式数量之多,在高级语言中是少见的。正是丰富的运算符和表达式使C语言功能十分完善。本节主要介绍运算符和表达式等方面的内容。

2.4.1 C语言运算符和表达式概述

1.运算符

C语言提供了比数学中+、-、×、÷组成的四则混合运算更加丰富的运算符,可以进行各种不同的运算,如算术运算、逻辑运算、关系运算等。

2.表达式

表达式是用运算符、括号将操作数连接起来所构成的式子。C语言的操作数包括常量、变量和函数值等。特殊情况下,一个单个变量或常量也可叫作表达式。例如:

     (x-y)*6/2+sqrt(6)

该式就是一个表达式,它包括的运算符有“+”“-”“*”“/”,操作数包括变量x、y,以及常量6和函数sqrt(6)。

表达式按照运算规则计算得到的一个结果,称为表达式的值。只有表达式的构成具有一定的意义时,才能得到期望的结果。

在表达式中,如果运算符的操作对象只有一个,就称为单目运算符,如取正运算符“+”、取负运算符“-”等。

如果运算符的操作对象有两个,就成为双目运算符,如加法运算符“+”、减法运算符“-”、乘法运算符“*”等。C语言中的运算符大多数是双目运算符。

如果运算符的操作对象为3个,就称为三目运算符,如“a>b?4∶5”由三个操作数组成,即a>b、4、5。

2.4.2 算术运算符

1.算术运算符的分类

算术运算符主要是实现数学上的算术运算。用算术运算符、括号和操作数连接起来的,符合C语言语法规则的式子,即为算术表达式。算术表达式的值是一个数值型数据。

C语言中的算术运算符主要有单目运算符和双目运算符两类,如表2.4所示。

表2.4 算术运算符及含义

以下是算术表达式的例子。

X*y、(a+2)/c、(x+y)/8-(a/b)*7、i++、sin(x)+sin(y)

2.运算符的优先级和结合性

与数学中的四则运算规则一样,C语言中的表达式运算也是具有优先级的。在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理。

C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。例如算术运算符的结合性是自左至右,即先左后右。如有表达式a-b+c,则b应先与“-”结合,执行a-b运算,然后再执行+c的运算。这种自左至右的结合方向就称为“左结合性”。而自右至左的结合方向称为“右结合性”。最典型的右结合性运算符是赋值运算符。如x=y=z,由于“=”的右结合性,应先执行y=z运算,再执行x=(y=z)运算。

算术运算符的优先级如下:

(1)(*、/、%)>(+、-)。

(2)自加、自减运算先后顺序:++i或者--i,先运算自加或自减,然后再做其他运算;i++或者i--,需要先进行其他运算,再计算自加或自减。

其中单目运算符的结合性是右结合性,双目运算符的结合性是左结合性。

【例2.6】 算术运算符的使用。

程序代码如下:

例2.6程序的运行结果如图2.7所示。

图2.7 例2.6程序的运行结果

程序说明:

(1)本程序的设计目的是考察除法运算符“/”和取余运算符“%”的使用。

(2)取余运算符“%”要求参与运算的量均为整型。10%3的结果是1。

(3)对于除法运算符“/”,当参与运算的量均为整型时,结果也为整型,舍去小数。如果运算量中有一个是实型,则结果为双精度实型。因此,变量q1的最终结果是3,而变量q2的最后结果是3.33。

2.4.3 关系运算符

1.关系运算符的分类

关系运算符主要实现数据的比较运算,如大于、小于、不等于等。由关系运算符将两个表达式连接起来的式子,就叫关系表达式。关系表达式的值是一个逻辑值,即“真”或“假”,分别用1和0表示。

C语言中的关系运算符如表2.5所示。

表2.5 关系运算符及含义

例如:

a+b>c-d、x>3/2、'a'+1<c、-i-5*j==k+1

这些式子都是合法的关系表达式。由于表达式也可以是关系表达式,因此也允许出现嵌套的情况。例如:

a>(b>c)、a!=(c==d)

2.关系运算符的优先级

关系运算符都是双目运算符,其结合性均为左结合。关系运算符的优先级低于算术运算符,高于赋值运算符。在六个关系运算符中,<、<=、>、>=的优先级相同,高于==和!=,而==和!=的优先级相同。

(>、>=、<、<=)>(==、!=)

2.4.4 逻辑运算符

1.逻辑运算符的分类

逻辑运算符用来实现逻辑判断功能,一般是对两个关系表达式的结果或逻辑值进行判断,如判断2>5和6<4是否同时成立等。

C语言中的逻辑运算符只有3个,即逻辑与(&&)、逻辑或(||)和逻辑非(!),其中逻辑与和逻辑或是双目运算符,逻辑非是单目运算符。

由逻辑运算符连接关系表达式或其他任意数值型表达式构成的式子就叫作逻辑表达式。逻辑表达式的值是一个逻辑值,用1(逻辑真)或0(逻辑假)表示。

因为C语言规定任何非0值都被视为逻辑真,而0视为逻辑假,因此逻辑运算符也可以连接数值型表达式,运算结果也是1或0。

逻辑运算符的分类及含义如表2.6所示。

表2.6 逻辑运算符的分类及含义

2.逻辑运算符的优先级和结合性

三个逻辑运算符中,逻辑非“!”的优先级最高,具有右结合性;其次是逻辑与“&&”;最后是逻辑或“||”。逻辑与和逻辑或都具有左结合性。它们的优先级如下:

!>&&>||

当一个复杂的表达式中既有算术运算符、关系运算符,还有逻辑运算符时,它们之间的优先级如下:

算术运算符>关系运算符>逻辑运算符

按照运算符的优先顺序可以得出:

a>b&& c>d等价于(a>b)&&(c>d)

!b==c||d<a等价于((!b)==c)||(d<a)

a+b>c&&x+y<b等价于((a+b)>c)&&((x+y)<b)

2.4.5 赋值运算符

1.基本赋值运算符

在以前的实例中,我们用到了大量的赋值运算符。赋值运算符的作用就是将某个数值存储到一个变量中。在C语言中,“=”符号称为赋值运算符,由赋值运算符组成的表达式称为赋值表达式,赋值表达式的值就是最左边变量所得到的新值。赋值表达式的格式如下:

     变量=表达式;

格式说明:

(1)赋值表达式的功能是计算表达式的值再赋予左边的变量,确切地说,是把数据放入以该变量为标识的存储单元去。

(2)赋值号右边必须是符合C语言规定的合法表达式。

(3)赋值运算符的左边只能是变量,而不能是表达式。如a+b=10是不合法的赋值表达式。

(4)在C语言中,把“=”定义为运算符,从而组成赋值表达式。凡是表达式可以出现的地方均可出现赋值表达式。例如,“x=(a=5)+(b=8)”是合法的赋值表达式,它的意义是把5赋予a,8赋予b,再把a、b相加,得到的和赋予x,故x应等于13。

(5)在C语言中也可以组成赋值语句,按照C语言规定,任何表达式在其末尾加上分号就构成为语句。因此如“x=8;a=b=c=5;”都是赋值语句。

(6)赋值运算符的优先级别只高于逗号运算符,比其他任何运算符的优先级都低。赋值运算符具有右结合性。因此“a=b=c=5”可理解为“a=(b=(c=5))”。

2.复合赋值运算符

在C语言中,赋值运算符还可以和其他二目运算符组合,形成复合赋值运算符。如+=、-=、*=等。由这些复合赋值运算符组成的表达式就称为复合赋值表达式。

构成复合赋值表达式的一般形式为

     变量双目运算符=表达式

它等效于:

     变量=变量运算符表达式

这里的运算符指的是二目算术运算符和以后要学到的二目位运算符。

例如:

a-=2 等价于 a=a-2

a*=b+1 等价于 a=a*(b+1)

a%=b 等价于 a=a%b

【例2.7】 赋值运算符的使用。

程序代码如下:

例2.7程序的运行结果如图2.8所示。

图2.8 例2.7程序的运行结果

程序说明:

(1)复合赋值表达式“a+=1;”是将变量a的ASCII码值加1,然后再赋值给a,此时,a的数值正好是字符'B'的ASCII码,因此输出的是字符'B'。

(2)复合赋值表达式“b-=2;c*=5;d/=2;e%=6;”分别是将变量b的值减2之后的值赋值给b;变量c的值乘以5之后的值赋值给c;变量d的值除以2之后的值赋值给d;变量e的值模除6之后的余数赋值给e。

(3)对于“a=b=c=d=e=99;”语句,由赋值运算符的右结合性,先将99赋值给变量e,然后将表达式“e=99”的值(即变量e的新值99),然后依次类推。最后将表达式“b=c=d=e=99”的值赋值给变量a,因为99正好是字符'c'的ASCII码值,所以在按字符格式输出的时候,输出的是字符'c'。

(4)对于“f=(c=2)*(d=e+8);”语句,根据赋值运算符的优先级,先将2赋值给变量c,然后将“e+8”的值赋值给变量d;再将变量c和d值相乘,得到的结果214赋值给变量f。

3.赋值运算中的类型转换

在赋值运算中,只有在赋值号右侧表达式的类型与左侧变量类型完全一致时,赋值操作才能进行。如果赋值运算符两边的数据类型不相同,系统将自动进行类型转换,即把赋值号右边的类型换成左边的类型。这种转换仅限于数值数据之间,通常称为“赋值兼容”,如整数和浮点数、整数和字符。具体规定如下:

(1)实型赋予整型,舍去小数部分。

(2)整型赋予实型,数值不变,但将以浮点形式存放,即增加小数部分(小数部分的值为0)。

(3)字符型赋予整型,由于字符型为一个字节,而整型为两个字节,故将字符的ASCII码值放到整型量的低八位中,高八位为0。整型赋予字符型,只把低八位赋予字符量。

【例2.8】 赋值运算类型转换的使用。

程序代码如下:

例2.8程序的运行结果如图2.9所示。

图2.9 例2.8程序的运行结果

程序说明:

(1)本例表明了上述赋值运算中类型转换的规则。

(2)a为整型,赋予实型量y的值8.88后,只取整数8。

(3)x为实型,赋予整型量b值322,后增加了小数部分。

(4)字符型量c1赋予a变为整型,整型量b赋予c2后取其低八位成为字符型(b的低八位为01000010,即十进制66,按ASCII码对应于字符B)。

2.4.6 自加、自减运算符

自加(++)和自减(--)运算符是C语言中经常使用的两个单目算术运算符,其功能是变量的值自增1和自减1。

自加和自减运算符的运算对象可以是整型变量和实型变量,但不能是常量和表达式,因为不能给常量或表达式赋值。如++3、(a+b)--都是错误的。

根据自加和自减运算符在变量前后的位置不同,可有以下几种形式。

++i i自增1后再参与其他运算。

--i i自减1后再参与其他运算。

i++ i参与运算后,i的值再自增1。

i-- i参与运算后,i的值再自减1。

一定要注意自加和自减运算符的不同位置会带来不同的运算结果,例如,假设整型变量i的值为1,则使用“a=i++;”语句,a的值为1;使用“a=++i;”语句,a的值为2。

自加和自减运算符的优先级和取正运算符(+)和取负运算符(-)的级别相同,但高于加、减、乘、除和取余等二目算术运算符。

【例2.9】 自加自减运算符的使用。

程序代码如下:

例2.9程序的运行结果如图2.10所示。

图2.10 例2.9程序的运行结果

程序说明:

(1)“a=b=c=d=e=f=g=h=8;”赋值语句是将数值8依次赋值给a、b、c、d、e、f、g、h这8个整型变量。

(2)因为++a、--b的运算符在前,所以输出的时候,都是先将两个变量进行加1和减1之后再输出,因此输出9和7。

(3)因为c++、d--的运算符在后,所以是先输出,再对两个变量进行加1和减1操作,因此输出8和8。

(4)因为“-”和自加、自减运算符的优先级相同,结合性都是右结合性,及从右向左运算,所以,“-e++”等价于“-(e++)”;“-f--”等价于“-(f--)”,因此输出8和8。

(5)因为“g++*9”的自加运算符在后,先将变量g的值乘9,然后再将g自增1,所以输出的结果是72。而“++h*9”的自加运算符在前,先将变量h自增1,再将增1后的结果乘9,所以输出的结果是81。

2.4.7 条件运算符

条件运算符由“?”“:”两个运算符组成,是C语言中唯一的三目运算符,要求有三个运算对象。由条件运算符组成的表达式称为条件表达式,其格式如下:

     表达式1?  表达式2:表达式3

格式说明如下。

(1)条件表达式的求值规则为:如果表达式1的值为真,则以表达式2的值作为条件表达式的值,否则以表达式3的值作为整个条件表达式的值。

(2)条件运算符的运算优先级低于关系运算符和算术运算符,但高于赋值符。因此条件表达式通常用于赋值语句之中。例如:

     y=x> 10?100:200

该语句的功能是:如果x>10为真,则把100赋予y,否则把200赋予y。

(3)条件运算符“?”和“:”是一对运算符,不能分开单独使用。

(4)条件运算符的结合方向是自右至左。

例如:

     a> b?a:c> d?c:d

应理解为

     a> b?a:(c> d?c:d)

这也就是条件表达式嵌套的情形,即其中的表达式3又是一个条件表达式。

【例2.10】 判断一个变量的值,如果其值大于0,则把它扩大10倍,否则将其值改为-1。

问题分析:根据示例描述,设变量为X,则可把问题概括为如下式子。

该式子正好符合条件表达式的计算规则。

程序代码如下:

例2.10程序的运行结果如图2.11所示。

图2.11 例2.10程序的运行结果

程序说明:

(1)第一个输出语句输出原来X的值。

(2)因为X>0,根据条件表达式的运算结果,第二条输出语句输出X的值为原来值的10倍,即50。

2.4.8 位运算符

C语言提供了位运算符,可以对一个变量的每一个二进制位进行操作。在编写系统软件,特别是驱动程序的时候,这些位运算符非常有用。

位运算符的操作对象只能是整型或字符型数据,不能是其他类型数据。

1.整数在内存中的存放

我们知道,数据在计算机中都是以二进制的形式存储的,最基本的单位是二进制位1比特(1 bit),8个二进制位是1字节(1 byte)。

例如,一个整型变量在内存中占2字节,即16位。以10为例,它的存放示意图如下:

实际上,整数数据在内存中是以补码表示的。

(1)正数的补码和原码相同。

(2)负数的补码:将该数的绝对值的二进制形式按位取反再加1。

例如,求-10的补码。

10的原码:

取反:

再加1,得-10的补码:

由此可知,左面的第一位是表示符号的。

2.位运算符的分类

根据位操作的需要,C语言提供了6种位运算符,如表2.7所示。

表2.7 位运算符及含义

位运算符的双目运算符具有左结合性,单目运算符具有右结合性,其中的优先级如下:

(~)>(<<、>>)>(&)>(^)>(|)

3.二进制位的逻辑运算

位运算符中,按位取反运算符(~)、按位与运算符(&)、按位异或运算符(^)、按位或运算符(|)都是对二进制位做逻辑运算,可以称为位逻辑运算符。

(1)按位取反运算符。求反运算符“~”为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按位求反。其运算规则如表2.8所示(这里设a为二进制的1位)。

表2.8 按位取反运算符的运算规则

(2)按位与运算符。按位与运算符“&”是双目运算符。其功能是参与运算的两操作数各对应的二进制位相与。只有对应的2个二进制位均为1时,结果位才为1,否则为0。其运算规则如表2.9所示(这里设a、b分别是二进制的1位)。

表2.9 按位与运算符的运算规则

(3)按位异或运算符。按位异或运算符“^”是双目运算符。其功能是参与运算的两操作数各对应的二进制位相异或,当2对应的二进制位相异时,结果为1。运算规则如表2.10所示(这里设a、b分别是二进制的1位)。

表2.10 按位异或运算符的运算规则

(4)按位或运算符。按位或运算符“|”是双目运算符。其功能是参与运算的两操作数各对应的二进制位相或。只要对应的2个二进制位有一个为1时,结果位就为1。运算规则如表2.11所示(这里设a、b分别是二进制的1位)。

表2.11 按位或运算符的运算规则

4.移位运算符

移位运算符用于实现二进制位的顺序向左或向右移位。

(1)左移位运算符。左移位运算符“<<”是双目运算符,其功能把“<<”左边的操作数的各二进制位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。

左移位运算符的格式如下:

     a<<n;

格式说明:

①a表示被移动的数据,可以是一个char型或整型的变量。

②n表示移动的位数,可以是一个整数型的常量、变量或表达式。

例如:

     a<<5;

该语句的功能是把a的各二进位向顺次向左移动5位。如a=00000011(十进制数3),左移5位后为01100000(十进制数96,即扩大32倍)。

(2)右移位运算符。右移位运算符“>>”是双目运算符。其功能是把“>>”左边的操作数的各二进制位全部右移若干位,“>>”右边的数指定移动的位数。

对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补0;而为负数时,符号位为1,最高位是补0或是补1取决于编译系统的规定。Turbo C和很多系统规定为补1。

右移位运算符的格式如下:

     a> > n;

格式说明:

①a表示被移动的数据,可以是一个char型或整型的变量。

②n表示移动的位数,可以是一个整数型的常量、变量或表达式。

例如:

     a> > 3;

该语句的功能是把a的各二进位向顺次向右移动3位。如a=01100000(十进制数96),右移3位后为00001100(十进制数12,即缩小8倍)。

2.4.9 逗号运算符

“,”是C语言提供的一种特殊运算符,用逗号将表达式连接起来的式子称为逗号表达式。逗号表达式的一般格式如下:

     表达式1,表达式2,...,表达式n

格式说明:

(1)逗号运算符的结合性为左结合性,因此逗号表达式将从左到右进行运算,即先计算表达式1,最后计算表达式n。最后一个表达式的值就是此逗号表达式的值。如逗号表达式(i=3,i++,++i,i+5)的值是10,变量i的值为5。

(2)在所有运算符中,逗号运算符的优先级别最低。

【例2.11】 逗号表达式的使用。

程序代码如下:

例2.11程序的运行结果如图2.12所示。

图2.12 例2.11程序的运行结果

程序说明:由于逗号运算符的优先级最低,而且具有左结合性,因此“y=a+b,b+c;”的表达式的值是第二个表达式的值10,而y值是6。

“z=(a+b,b+c);”语句将逗号表达式包含在括号内,因此,z的值就是逗号表达式的值10。