购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第3章
运算符与表达式

认识了C++的基本数据类型后,接下来就要操作这些数据了。而要操作数据,就离不开运算符和表达式。本章将引导读者学习C++运算符和表达式的相关知识,包括赋值运算、算术运算、关系运算、逻辑运算、逗号运算、位运算、移位运算、sizeof运算等,同时了解数据类型自动转换和强制转换的知识,并且通过实例进行相应的练习,以加深印象。

本章知识架构及重难点如下:

3.1 表达式

C++中,表达式由运算符、操作数、括号等组成。简单的表达式只包含一个数值,复杂的表达式包含多个操作数和多个运算符。例如,以下均为合法的表达式。

3.1415926
i + 11
x >= y
100 >> 2
iBase+(iPay*iDay)

C++中的运算符多数继承自C语言,新增的运算符仅有“::”(作用域运算符)和“->”(指向运算符)。根据操作数的数量,运算符可分为单目运算符、双目运算符和三目运算符。

复杂表达式中,运算符执行的先后顺序由它们的优先级和结合性决定。表达式的值的数据类型由运算符的种类和操作数的数据类型决定。

表达式可出现在赋值语句的右侧或函数的参数中。表达式可返回一个结果,其数据类型取决于组成表达式的变量和常量的类型。

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

程序中常常遇到的赋值符号“=”就是赋值运算符,其作用就是将一个数据赋给一个变量。例如:

Age=520;

赋值表达式的一般形式为:

数据类型 变量名 = 表达式;

定义变量的同时直接为其赋值的操作称为赋初值,也就是变量的初始化。先定义变量,再进行变量的赋值操作也是可以的。例如:

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

3.3.1 算术运算符

算术运算符包括4个单目运算符(取正值、取负值、自增、自减)和5个双目运算符(加法、减法、乘法、除法和求余),如表3.1所示。

表3.1 算术运算符

说明

expr表示使用运算符的对象,可以是表达式、变量或常量。

其中,“+”“−”作为加法和减法运算符用时为双目运算符,作为正值、负值运算符用时为单目运算符;求余运算符“%”用于计算两个整数相除得到的余数,运算符的两侧均为整数,如7%4的结果是3;使用除法运算符“/”时,除数不能为0,否则会产生溢出,处理器抛出异常;自增运算符“++”和自减运算符“−−”对变量的操作效果分别是使变量增加1和减少1。

说明

表达式求值时,会按照运算符的优先级别从高到低依次执行。单目正和单目负的优先级最高;“*”“/”“%”的优先级高于“+”“−”;括号在所有运算符中优先级最高,可以改变运算顺序。当算术运算符的优先级相同时,结合方向为“自左向右”。这和数学中的计算顺序是一样的。

3.3.2 算术表达式

使用算术运算符的表达式称为算术表达式,例如:

(3+5)/Rate
Top-Bottom+1
Height * Width

进行四则混合运算时,不同的数据类型会先自动转换成同一类型,然后再进行运算。

(1)若所有操作数的数据类型相同,则表达式运算结果的数据类型和操作数的数据类型相同。例如,两个整型数相加,其结果仍然是一个整型数。

(2)若操作数的数据类型不同,则表达式运算结果的数据类型取最高的数据类型,以保证数据精度不发生损失。例如,混合运算10+'a'−1.5+3.2*6中出现了整型、字符型、浮点型,表达式的运算结果是浮点型。

3.3.3 自增、自减运算符

自增运算符“++”和自减运算符“−−”对变量的操作效果分别是使变量增加1和减少1。

自增、自减运算符可以放在变量的前面,如++expr、--expr,这种情况称为前缀运算;也可以放在变量的后面,如expr++和expr--,这种情况称为后缀运算。两者对运算结果的影响是不一样的。

前缀运算中,变量先完成自增或自减运算,再以增减后的结果参与表达式运算;

后缀运算中,变量先参加表达式运算,之后再进行自增或自减。

当自增、自减运算符出现在表达式内部,作为运算的一部分时,前缀和后缀的运算结果差异很大。

上述代码中,a、b都被赋予初值1,c的结果为1,d的结果为2。这是因为代码“c=a++;”中,a先进行自增运算,变为2,然后将2赋给b;代码“d=++b;”中,b先进行赋值操作,d得到赋值1,然后b进行自增操作,变为2。读者要仔细体会其中的差别。

编程训练(答案位置:资源包\TM\sl\3\编程训练\)

【训练1】计算基本收益 某基金年化利率为3.5%,现存入1万元本金,请问一天后连本带利有多少钱。计算公式:一天收益=本金*年化利率/ 365。

3.4 关系运算符和关系表达式

数学中,经常需要比较两个数的大小。C++中,关系运算符的作用就是判断两个对象的大小关系。

3.4.1 关系运算符

关系运算符包括大于、大于或等于、小于、小于或等于、等于和不等于6种运算符,如表3.2所示。

表3.2 关系运算符

3.4.2 关系表达式

关系表达式的一般形式如下:

表达式1  关系运算符  表达式2

关系运算符可对两个表达式进行比较,返回一个真值或假值。真值为1,表示指定的关系成立;假值为0,表示指定的关系不成立。例如:

关系运算符常用来构造条件表达式,用在流程控制语句中。

关系运算符都是双目运算符,其结合性为从左向右。关系运算符的优先级低于算术运算符,高于赋值运算符。在6个关系运算符中,“<”“<=”“>”“>=”的优先级相同,高于“==”和“!=”。

误区警示

初学者要注意区分“=”和“==”,这两个运算符的含义截然不同。“=”是赋值运算符,用于将等号右边的值或表达式赋给左边;“==”是等于运算符,用于判断左右两侧的值是否相等。

例如,“i==3;”中“==”是关系运算符,“i=3;”中“=”是赋值运算符。

编程训练(答案位置:资源包\TM\sl\3\编程训练\)

【训练2】面积之争 使用关系运算符比较扇形和圆锥谁的面积大(半径相等)。要求定义几个变量,用来保存半径、高等数据,输出结果如下:

圆锥的面积比扇形的面积大
圆锥的侧面积是18.84平方厘米
扇形的面积是3.50平方厘米

3.5 逻辑运算符和逻辑表达式

逻辑运算符可根据表达式的真、假属性返回真值或假值。C++中,表达式的值非0,表示其值为真。非0的值参与逻辑运算,等价于1;假值总是为0。

3.5.1 逻辑运算符

逻辑运算符可对真和假两种逻辑值进行运算,运算后的结果仍是一个逻辑值。逻辑运算符有3个,分别是逻辑与、逻辑或和逻辑非,如表3.3所示。

表3.3 逻辑运算符

逻辑运算中,非0代表真,0代表假。逻辑运算后,会返回一个结果,1代表真,0代表假。逻辑运算过程如表3.4所示。

表3.4 逻辑运算过程

逻辑运算可以嵌套,当存在多个逻辑运算符时,其结合性为自左向右。例如,(A||B)&&C等同于A||B&&C,即先对A和B进行逻辑或运算,得到的结果再与C进行逻辑与运算。

3.5.2 逻辑表达式

逻辑表达式的值也是逻辑型,只能取真或假。其一般形式如下:

表达式1  逻辑运算符  表达式2

使用逻辑运算符,可以将多个关系表达式的结果合并在一起进行判断。逻辑表达式的值则是各种逻辑运算的最后值。

使用逻辑表达式的注意事项如下:

(1)逻辑运算符两侧的表达式结果,除可以是0和非0的整数外,还可以是其他任何类型的数据,如实型、字符型等。

(2)逻辑运算符两侧的表达式,并不都需要进行判定或求解。

对于逻辑与运算,如果表达式1判定为假,系统不再判定或求解表达式2。

对于逻辑或运算,如果表达式1判定为真,系统不再判定或求解表达式2。

【实例3.1】 能否应聘。 (实例位置:资源包\TM\sl\3\1)

某模特公司要求应聘者的年龄在20~30岁之间。假设有一名应聘者,年龄为32岁,判断他是否满足应聘要求。代码如下:

程序运行结果如图3.1所示。

图3.1 程序运行结果

编程训练(答案位置:资源包\TM\sl\3\编程训练\)

【训练3】判断用户是否合法 明日学院网站可以使用账户名、手机号或电子邮箱地址登录。已知服务器中有如下记录,账户名:张三,手机号:1234567890,电子邮箱:zhangsan@163.com。判断某用户是否可以登录。

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

C++中,逗号运算符“,”用于在单个语句中同时执行多个操作,并返回最后一个表达式的值。逗号运算符的优先级最低,结合方向为自左至右。

逗号表达式又称为顺序求值表达式,一般形式如下:

表达式1,表达式2,…,表达式n

其求解过程为:先执行表达式1,再执行表达式2,……,最后执行表达式n,整个逗号表达式的值和类型由表达式n决定。例如:

a=3*5, a*4

上述逗号表达式中,赋值运算符的优先级别高于逗号运算符,因此会先执行表达式a=3*5,得出a的值为15;然后执行表达式a*4,结果为60。最终,整个逗号表达式的值为60。

说明

程序中使用逗号表达式,通常是为了取得各表达式的值,而不是求解整个逗号表达式的值。也就是说,逗号运算符最大的作用是把多个表达式“串联”起来。逗号运算符常用于for循环语句中。

3.7 位运算符

C++中,位运算的对象只能是整型或字符型数据。位运算符可对其二进制位进行操作,包括位与“&”、位或“|”、位异或“^”和取反“~”4种运算,如表3.5所示。

表3.5 位运算符

在3个双目运算符中,“&”的优先级最高,“|”次之,“~”最低。

1.位与运算

位与运算中,需将操作数转换成二进制表示方式,从低位(最右边)到高位对齐,逐位求与。若两个操作数对象同一位都为1,则结果对应位为1,否则结果对应位为0。

例如,12和8位与运算后得到的结果是8。

2.位或运算

位或运算中,需将操作数转换成二进制表示方式,从低位(最右边)到高位对齐,逐位求或。若两个操作数对象同一位都为0,则结果对应位为0,否则结果对应位为1。

例如,4和8位或运算后得到的结果是12。

3.位异或运算

位异或运算中,需将操作数转换成二进制表示方式,从低位(最右边)到高位对齐,逐位求异或。若两个操作数对象同一位不相同,则结果对应位为1,否则结果中对应位为0。

例如,31和22位异或运算后得到的结果是9。

4.取反运算

取反运算中,先将操作数转换成二进制表示方式,然后将各二进制位1变为0,0变为1。

例如,41883取反运算后得到的结果是23652。

说明

十进制数在用二进制表示时有原码、反码、补码等表示方式。

3.8 移位运算符

C++中,移位运算的对象只能是整型或字符型数据。移位运算符有两个,分别是左移运算符“<<”和右移运算符“>>”,它们都是双目运算符。例如,a<<2表示将变量a的二进制位左移2位,b>>1表示将变量b的二进制位右移1位。

下面以短整型数据(内存中占2个字节,16位)为例,介绍左移运算和右移运算的基础知识。

1.左移运算

左移运算,指将一个二进制数向左移动指定位数,左边(高位端)溢出的位被丢弃,右边(低位端)的空位用0补充。

当数值不存在溢出风险时,左移运算的效果相当于原数值乘以2的幂,如图3.2所示。

图3.2 左移位运算(不存在溢出风险)

当需要移位的数值较大时,会发生数据溢出并被舍去。例如,操作数41883的二进制是1010 0011 1001 1011,左移一位后会变成18230,左移两位后会变成36460,如图3.3所示。

图3.3 41883的左移位运算

2.右移运算

右移运算,指将一个二进制数向右移动指定的位数,右边(低位端)溢出的位被丢弃,左边(高位端)的空位用0填充,或者用被移位操作数的符号位填充。运算结果和编译器有关,在使用补码的机器中,正数的符号位为0,负数的符号位为1。

当数值不存在溢出风险时,右移位运算的效果相当于原数值除以2的幂,如图3.4所示。

图3.4 右移位运算(不存在溢出风险)

同样,右移位运算有时也会发生数据溢出。例如,操作数41883的二进制是1010 0011 1001 1011,右移一位变成20941,右移两位变成10470,运行过程如图3.5所示。

图3.5 41883的右移位运算

【实例3.2】 将0x40左移运算。 (实例位置:资源包\TM\sl\3\2)

本实例中,定义一个变量并赋值0x40(对应十进制数64),将这个值进行左移一位运算,具体代码如下:

程序运行结果如下:

128

由于位运算的速度很快,在程序中遇到表达式乘以或除以2的幂的情况,一般采用位运算来代替。

【实例3.3】 输出十六进制的高位和低位。 (实例位置:资源包\TM\sl\3\3)

本实例中,定义一个长整型变量并赋值,利用位与运算和移位运算,计算这个十六进制的高4位和低4位,具体代码如下:

程序运行结果如图3.6所示。

编程训练(答案位置:资源包\TM\sl\3\编程训练\)

【训练4】1024%8的不同解法 使用移位运算符和算术运算符,计算“1024 % 8”的结果。

图3.6 输出高4位和低4位

3.9 复合赋值运算符

复合赋值运算符都是双目运算符,相当于一个简单赋值运算符和其他运算符的组合。C++中提供了很多复合赋值运算符,如表3.6所示。

表3.6 复合赋值运算符

复合赋值运算不但书写形式简洁、紧凑,运算也非常高效,编译器在生成目标代码时能够直接进行优化。

复合赋值运算符可将中间的运算结果返回,作为表达式的值。例如,下述代码中,a*=5等价于a=a*5,a*5的运算结果30作为临时变量赋给了变量a。

int a=6;
a*=5;

3.10 sizeof运算符

sizeof运算符用于计算指定数据类型或表达式结果在内存中占用的字节数,有以下两种语法形式:

sizeof(类型说明符)
sizeof(表达式)

sizeof运算符是一个看起来非常像函数的运算符,下面来看几个示例。

注意

由于CPU寄存器的位数不同,不同计算机上同种数据类型占用的内存字节数目可能不同。

3.11 运算符的优先级和结合性

运算符的优先级决定了表达式中各项运算执行的先后顺序。高优先级的运算符要先于低优先级的运算符进行运算。例如,表达式a+b*c中,先计算b*c,得到结果再与a相加,这说明“*”运算符的优先级高于“+”运算符。当表达式中出现括号时,会改变运算的先后顺序,如表达式(a+b)*c中,先计算括号中的表达式,再计算括号外的表达式,这说明“( )”运算符的优先级是最高的。

运算符的结合方式有两种:自左向右结合(→)和自右向左结合(←)。自左向右结合表示运算符优先与其左边的表达式结合,如加法、减法、乘法、除法运算等;自右向左结合表示运算符优先与其右边的表达式结合,如自增、自减、复合赋值运算等。

同一优先级的运算符,运算顺序由结合方向决定。例如,表达式1*2/3中,“*”和“/”的优先级别相同,其结合方向自左向右,因此该表达式等价于(1*2)/3。

C++中,各运算符的优先级和结合性如表3.7所示。

表3.7 C++运算符的优先级和结合性

3.12 数据类型转换

变量的数据类型转换方式有两种:一种是隐式类型转换(又称为自动类型转换),一种是强制类型转换。

3.12.1 隐式类型转换

隐式类型转换发生在不同数据类型的量进行赋值或混合运算时,由编译系统自动完成,并遵循以下规则。

(1)赋值表达式中,“=”右边量的数据类型会自动转换为左边量的数据类型。如果右边量的数据类型精度比较高,则赋值后会降低精度,丢失一部分数据(丢失部分按“四舍五入”规则向前舍入)。

(2)算术表达式中,若参与运算的多个量的数据类型不同,则会先转换成统一类型,然后再进行运算。转换原则是:将数据由低精度类型转换为高精度类型,以保证精度不降低。

int型和long型混合运算时,先把int型转换为long型,再进行运算。

浮点数都是以double型进行运算的,哪怕是仅含float型的表达式,也会先转换成double型,再进行运算。

char型和short型运算时,必须先转换成int型。

数据类型隐式转换的顺序如图3.7所示。

图3.7 数据类型隐式转换的顺序

【实例3.4】 数据类型隐式转换。 (实例位置:资源包\TM\sl\3\4)

本实例中,定义一个字符型、一个整型和一个浮点型变量,利用隐式类型转换进行计算,具体代码如下:

程序运行结果如下:

115.485000
误区警示

C++中,整数的默认类型是int,浮点数的默认类型是double。如果一个计算中数字都是整数,则计算结果默认也是int型。但这样有时会出现一些“意想不到”的错误。

例如,计算5除以2,如果这么写:

     double b = 5 / 2;

计算后会发现b的值居然是2.0,而不是2.5。要想得到正确的答案,应该这样修改:

     double b = 5.0 / 2;  //把其中一个数改为浮点数,以保证精度

3.12.2 强制类型转换

强制类型转换是通过类型转换说明符来实现的,其一般形式如下:

类型说明符 (表达式)
(类型说明符) 表达式

其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。例如,“(float) x”表示把x强制转换为float型;“(int)(x+y)”表示把x+y的结果强制转换为int型;int(1.3)表示将1.3强制转换为int型,最终结果为1。

强制类型转换不改变数据说明时对该变量定义的类型。例如,下面的代码中x仍为double型。

double x;
(int)x;

使用强制类型转换,意味着将由程序员负责保证类型转换的正确性。

【实例3.5】 计算载货区摆放箱子的数量。 (实例位置:资源包\TM\sl\3\5)

一辆货车运输箱子,载货区宽2m,长4m,一个箱子宽1.5m,长1.5m,请问载货区一层可以放多少个箱子。注意,箱子必须以一个整体存在,不存在半个箱子。实现的具体代码如下:

程序运行结果如下:

载货区一层可以放2个箱子
误区警示

如果要对某个表达式进行强制类型转换,需要用括号将表达式括起来,否则将只对表达式中的第一个变量或常量进行强制类型转换。例如:

编程训练(答案位置:资源包\TM\sl\3\编程训练\)

【训练5】模拟超市抹零 购物时,如果买的东西总价多出1角、2角,收银员会抹掉,只收取整钱。模拟超市抹零结账场景,输出结果如下:

实际应付313.04元
抹零之后支付313元

3.13 判断左值与右值

C++中,表达式语句可以分为左值与右值。左值是内存中持续储存的数据,表达式执行完毕后会持久存在;右值是临时储存的计算结果,表达式执行完毕后就不再存在。

程序中声明的独立变量都是左值,例如:

int k;
short p;
char a;

下述代码中,a、b、c都是左值;c-b是一个储存减法运算结果的临时数据,是一个右值;a++实质上是编译器中一个临时变量执行了自增,是一个右值;++a恰好相反,是直接对a执行自增,是一个左值;同理,c--是一个右值。

int a = 0;
int b = 2;
int c = 3;
a = c-b;
b = a++;
c = ++a;
c--;

注意,若表达式的结果不是一个左值,那么一定是一个右值。

说明

后缀自增、自减运算中,编译器先生成一个变量的临时复制,再对其进行自增或自减,最后返回临时复制内容,因此a++、a--是右值。前缀自增、自减运算中,直接对a执行自增、自减运算并返回a,因此是左值。

3.14 实践与练习

答案位置:(资源包\TM\sl\3\实践与练习\)

综合练习1:港珠澳大桥的长度 港珠澳大桥位于广东省伶仃洋区域内,桥隧全长55千米,其中主桥29.6千米,香港口岸至珠澳口岸41.6千米;桥面为双向六车道高速公路,设计速度100千米/小时。将港珠澳大桥的全长转换成古代的丈、尺单位(1丈=10尺,3尺=1米)表示。输出结果如下:

港珠澳大桥全长378600.000000尺
港珠澳大桥全长37860.000000丈

综合训练2:换季买鞋 买鞋时,如果37码鞋小,38码鞋大,则说明脚的尺寸是37.5码。因为没有37.5的鞋码,所以我们一般买38码的鞋子。利用强制类型转换来模拟此场景,输出结果如下:

您的脚是37.5码的尺寸
您应该买38码的鞋子

综合练习3:计算表达式的值 使用复合赋值运算符计算a+=a*=a/=a-6。其中,a的初始值是12。输出结果如下:

◇-◇-◇-◇-◇-◇-◇-◇-◇
   the result is 8
◇-◇-◇-◇-◇-◇-◇-◇-◇

综合练习4:单细胞细菌繁殖实验 生物实验室做单细胞细菌繁殖实验,每代细菌数量都会成倍数增长,一代菌落中只有一个细菌,二代菌落中分裂成两个细菌,三代菌落中分裂成4个细菌,以此类推,计算第五代菌落中的细菌数量。输出结果如下: PNpJrMrMgC2lfXnt+NFVc2GHxbt4ko6+e2l0vYZPIXkTZ+UUPsoX8ILPsyCTnUts

点击中间区域
呼出菜单
上一章
目录
下一章
×