C语言程序设计任务驱动式教程(第2版)(微课版)
上QQ阅读APP看书,第一时间看更新

任务3 分离数字问题——运算符与表达式

工作任务

编写一个程序,从键盘输入一个三位整数,将其逆序输出。例如,输入123,输出321。

思路指导

已知:一个三位整数存储到变量n中。

输出:将n逆序输出。

处理:将这个三位数分解,分别求出百位(n/100)、十位(n/10%10)、个位(n%10),然后逆序输出。

微课视频

数字分离问题——运算符及表达式

相关知识

运算符与表达式

运算符:运算符是表示各种运算的符号。

表达式:使用运算符将常量、变量、函数连接起来,构成表达式。

C语言运算符内容丰富,范围广泛,因为C把除了控制语句和输入/输出以外几乎所有的基本操作都作为运算符处理,所以C语言运算符可以看作是操作符。C语言丰富的运算符构成了C语言丰富的表达式。

在C语言中除了提供一般高级语言的算术、关系、逻辑运算符外,还提供赋值运算符、位操作运算符、自增自减运算符等。运算符的常见类型及其作用如表2-4所示。

表2-4 常见的运算符类型及其作用

本单元主要介绍算术运算符(包括自增自减运算符)、赋值运算符、逗号运算符、位运算符、sizeof运算符,其他运算符将在以后相关章节中结合有关内容陆续介绍。

1.算术运算符和算术表达式

(1)算术运算符。

说明

①两个整数相除的结果为整数。例如,5/3的值为1,舍去小数部分,而5.0/3的值为1.67(保留两位小数后)。

②求余也称为求模,要求运算符%两边的两个操作数均为整型,结果为两数相除所得的余数。例如,8%5的值为3。

(2)算术表达式。

用算术运算符和括号将运算对象(也称操作数)连接起来的、符合C语法规则的式子称为算术表达式。运算对象可以是常量、变量、函数等。

例如,下面是一个合法的C算术表达式。

说明

C语言算术表达式的书写形式与数学表达式的书写形式有一定的区别,如下所示。

①C语言算术表达式的乘号(*)不能省略。例如,数学式b2-4ac对应的C表达式应该写成:b *b -4*a *c。

②C语言表达式中只能出现字符集允许的字符。例如,数学式πr2对应的C表达式应该写成:PI*r *r(其中PI是已经定义的符号常量)。

③C语言算术表达式不允许有分子分母的形式(即所有字符必须写在同一行中)。例如,(a +b)/(c +d )。

(3)算术运算符的优先级与结合性。

①C语言规定了运算符的“优先级”和“结合性”,详见附录3运算符的优先级和结合方向表。在表达式求值时,先按运算符的“优先级别”从高到低依次执行。

例如,表达式a−b*c等价于a−(b*c),因为“*”“/”运算符的优先级高于“+”“−”运算符。

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

【思考】a−b+c,到底是(a−b)+c还是a−(b+c)?(b先与a参与运算还是先与c参与运算?)

查附录3可知:加+、减−运算优先级别相同,结合性为“自左向右”,也就是说,b先与左边的a结合。所以a−b+c等价于(a−b)+c。

左结合性(自左向右结合方向):运算对象先与左边的运算符结合。

右结合性(自右向左结合方向):运算对象先与右边的运算符结合。

③在书写多个运算符的表达式时,应当注意各个运算符的优先级,确保表达式中的运算符能以正确的顺序参与运算。对于复杂表达式,为了清晰起见,可以加圆括号“()”强制规定计算顺序(不要用{}和[])。可以使用多层圆括号,此时左右括号必须配对,运算时从内层括号开始,由内向外依次计算表达式的值。

2.赋值运算符和赋值表达式

(1)赋值运算符、赋值表达式。

赋值运算符:“=”是赋值运算符。

赋值表达式:由赋值运算符组成的表达式称为赋值表达式。一般形式为

赋值表达式的求解过程为:将赋值运算符右侧的表达式的值赋给左侧的变量,同时整个赋值表达式的值就是刚才所赋的值。

赋值的含义为:将赋值运算符右边表达式的值存放到左边变量名标识的存储单元中。

例如,x=10+y;执行赋值运算(操作),将10+y的值赋给变量x,同时整个表达式的值就是刚才所赋的值。

说明

赋值运算符左边必须是变量,右边可以是常量、变量、函数调用,或是由常量、变量、函数调用组成的表达式。

例如,x=10、y=x+10、y=func()都是合法的赋值表达式。

赋值符号“=”不同于数学的等号,它没有相等的含义(“==”代表判断是否相等)。

例如,在C语言中,x=x+1是合法的(数学上不合法),它的含义是取出变量x的值加1,再存放到变量x中。

C语言的赋值符号“=”除了表示一个赋值操作外,还是一个运算符,也就是说,赋值运算符完成赋值操作后,整个赋值表达式还会产生一个所赋的值,这个值还可以利用。

赋值表达式的求解过程如下。

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

②将赋值运算符右侧“表达式”的值赋值给左侧的变量。

③整个赋值表达式的值就是被赋值变量的值。

例如,分析x=y=z=3+5这个表达式。根据优先级:原式b0x=y=z=(3+5);根据结合性(从右向左):b0x=(y=(z=(3+5)))b0x=(y=(z=3+5))。

z=3+5:先计算3+5,得值8,将8赋值给变量z,z的值为8,(z=3+5)整个赋值表达式的值为8。

y=(z=3+5):将上面(z=3+5)整个赋值表达式的值8赋值给变量y,y的值为8,(y=(z=3+5))整个赋值表达式的值为8。

x=(y=(z=3+5)):将上面(y=(z=3+5))整个赋值表达式的值8赋值给变量z,z的值为8,整个表达式x=(y=(z=3+5))的值为8。

最后,x,y,z都等于8。

本例的运算步骤见表2-5。

表2-5 运算步骤

将赋值表达式作为表达式的一种,使赋值操作不仅可以出现在赋值语句中,而且可以以表达式的形式出现在其他语句中。

(2)复合赋值运算符。

在赋值符“=”之前加上某些运算符,可以构成复合赋值运算符,复合赋值运算符可以构成赋值表达式。C语言中的许多运算符可以与赋值运算符一起构成复合运算符,即

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

复合赋值表达式的一般形式等价于

例如:

注意:赋值运算符、复合赋值运算符的优先级比算术运算符低。

赋值运算符、赋值表达式举例。

例2.4 假如a=12,分析:a+=a−=a*a

(3)自增、自减运算符及表达式。

自增、自减运算符使变量的值增1或减1,形如:

其中,++I、−−i(前置运算):先自增、自减,再参与运算;i++,i−−(后置运算):先参与运算,再自增、自减。

例如,i=3,分析j=++i;j=i++;

j=++i;i先自增,再赋值给j,i的值是4,j的值是4。

j=i++;i先赋值给j,再自增,j的值是3,i的值是4。

自增、自减运算符只用于变量,而不能用于常量或表达式。

例如,6++、(a+b)++、(−i)++都不合法。

++、−−的结合方向是“自右向左”(与一般算术运算符不同)。

例如:−i++b0−(i++),合法。

自增、自减运算符常用于循环语句中,使循环变量自动加1,也用于指针变量,使指针指向下一个地址。

说明

不管是前缀++还是后缀++,对于变量的作用都是加1操作;但对于表达式来讲,++在前的表达式用的是变量加1以后的新值,++在后的表达式用的是变量原来的值。--运算符与++相同。

(4)有关表达式使用过程中的问题说明。

C运算符和表达式使用灵活,但是ANSI C并没有具体规定表达式中的子表达式的求值顺序,允许各编译系统自己安排。这可能导致有些表达式对不同编译系统有不同的解释,并导致最终结果不一致。

例如,i=3,表达式(i++)+(i++)+(i++)的值在VC++ 6.0中等价于3+4+5,在Turbo C中则等价于3+3+3。

C语言有的运算符为一个字符,有的由两个字符组成,C编译系统在处理时尽可能多地将若干字符组成一个运算符(在处理标识符、关键字时也按同一原则处理),如i+++j将解释为(i++)+j而不是i+(++j)。为避免误解,最好采用大家都能理解的写法,比如通过增加括号明确组合关系,改善可读性。

C语言中类似的问题还有函数调用时实参的求值顺序,C标准也无统一规定。

例如,i=3,printf("%d,%d",i,i++);

有些系统执行的结果为3,3;有些系统为4,3。

总之,不要写别人看不懂(难看懂),也不知道系统会怎样执行的程序。

3.逗号运算符和逗号表达式

C语言提供了一种特殊的运算符——逗号运算符(顺序求值运算符)。用它将两个或多个表达式连接起来,表示顺序求值(顺序处理)。用逗号连接起来的表达式称为逗号表达式。

例如,3+5,6+8。

逗号表达式的一般形式为

逗号表达式的求解过程是:自左向右,求解表达式1,求解表达式2……求解表达式n。整个逗号表达式的值是表达式n的值。

例如,逗号表达式3+5,6+8的值为14。

例2.5 分析a=3*5,a*4的值

查附录3可知,“=”运算符优先级高于“,”运算符(事实上,逗号运算符级别最低)。上面的表达式等价于:

所以整个表达式计算后值为60(其中a=15)。

例2.6 分析下列程序

运行结果为

逗号表达式主要用于将若干表达式“串联”起来,表示一个顺序的操作(计算)。在许多情况下,使用逗号表达式的目的只是想分别得到各个表达式的值,而并非一定需要得到和使用整个逗号表达式的值。

4.位运算符

位运算符是针对二进制数的每一位进行运算的符号,它是专门针对数字0和1进行操作的。C语言中的位运算符如表2-6所示。

表2-6 位运算符

接下来通过一些具体示例,详细介绍表2-6中的位运算符,为了方便描述,下面的运算都是针对byte类型的数,也就是1字节大小的数。

(1)与运算符“&”是将参与运算的两个二进制数进行“与”运算,如果两个二进制位都为1,则该位的运算结果为1,否则为0。

例如,将5和12进行与运算,5对应的二进制数为00000101,12对应的二进制数为00001100,具体演算过程如下。

运算结果为00000100,对应数值4。

(2)位运算符“|”是将参与运算的两个二进制数进行“或”运算,如果二进制位上有一个值为1,则该位的运行结果为1,否则为0。

例如,将5与12进行或运算,具体演算过程如下。

运算结果为00001101,对应数值13。

(3)位运算符“~”只针对一个操作数进行操作,如果二进制位是0,则取反值为1;如果是1,则取反值为0。

例如,将5进行取反运算,具体演算过程如下。

运算结果为11111010,对应数值−10。

(4)位运算符“^”是将参与运算的两个二进制数进行“异或”运算,如果二进制位相同,则值为0,否则为1。

例如,将5与12进行异或运算,具体演算过程如下。

运算结果为00001001,对应数值9。

(5)位运算符“<<”就是将操作数的所有二进制位向左移动一位。运算时,右边的空位补0,左边移走的部分舍去。

例如,一个byte类型的数字5用二进制表示为00000101,将它左移一位,具体演算过程如下。

运算结果为00001010,对应数值10。

(6)位运算符“>>”就是将操作数的所有二进制位向右移动一位。运算时,左边的空位根据原数的符号位补0或者1(原来是负数就补1,是正数就补0)。

例如,一个byte的数字5用二进制表示为00000101,将它右移一位,具体演算过程如下。

运算结果为00000010,对应数值2。

5.sizeof运算符

同一种数据类型在不同的编译系统中所占空间不一定相同,例如,在基于16位的编译系统中,int类型占2字节,而在32位的编译系统中,int类型占4字节。为了获取某一数据或数据类型在内存中所占的字节数,C语言提供了sizeof运算符,使用sizeof运算符获取数据字节数,其基本语法规则如下。

例2.7 计算每种数据类型所占内存的大小

运行结果如图2-4所示。

图2-4 例2.7运行结果

说明

C语言运算符又分为单目、双目、三目3种。进行运算时,只需要一个运算对象的运算符为单目运算符,如正号(+)、负号(-)、自增(++)、自减(--)等;进行运算时,需要两个运算对象的运算符为双目运算符,如算术运算符:加(+)、减(-)、乘(*)、除(/)等;C语言中只有一个三目运算符,运算时需要3个运算对象,即条件运算符(?:),见第4单元的拓展与提高。

任务实施

三位整数逆序输出,程序清单如下。

运行结果如图2-5所示。

图2-5 任务3运行结果

特别提示

(1)分离数字是C语言的基础算法之一,请读者认真理解并学会。

(2)“%”符号是求余运算符,%符号两边要求是整数。