Download presentation
Presentation is loading. Please wait.
1
第3章 运算符和表达式 3.2 表达式与表达式的 3.1 运算符与运算符的 计算 分类 --3.2.1 表达式 --1.算术运算符
第3章 运算符和表达式 3.2 表达式与表达式的 计算 表达式 复合表达式的 数据类型转换 本章小结 习题 3 3.1 运算符与运算符的 分类 --1.算术运算符 --2.关系运算符 --3.逻辑运算符 --4.条件运算符 --5.赋值运算符 --6.逗号运算符 --7.地址运算符 --8.sizeof运算符
2
与其他高级语言相比,C语言的运算符非常的丰富,这是C语言的特征之一。
要掌握表达式中每种运算符的功能、优先级、结合性及在使用中的注意事项。
3
3.1 运算符与运算符的分类 C语言运算符按其功能分为:算术运算符、关系运算符、逻辑运算符、位运算符、条件运算符、逗号运算符、赋值运算符、指针运算符等; 按其所在表达式中参与运算的操作数的数目来分,可分为:单目运算符、双目运算符和三目运算符。
4
算术运算符 表3.1列出了C语言所允许的算术运算符,其中+、-、*、/的含义与其他高级语言含义相同。算术运算符几乎可以用于全部的C语言数据类型。 +加法 -减法或单目取负 *乘法 /除法 %取余(模)--减 加 运算符“/” ① 两个整数相除,结果为整数,其值是商的整数部分,不允许四舍五入。 ② “0”不能做除数。
5
例3.1 #include <stdio.h> void main( ) { int x,y,z; x=5/2; y=-5/2; z=6/(-4); printf(″x=%d,y=%d,z=%d″,x,y,z); } 程序运行结果为:x=2,y=-2,z=-1
6
(2) 运算符“%” ① 只适用于整型数据。 ② %表示求两数相除后得到的余数。 ③ 余数的符号与被除数的符号相同。 如:10%5=0; %6=4; %3=-1; 6%(-4)=2 本例中-10%3结果的符号应与被除数的符号相同,为负号。而6%(-4) 结果的符号也应遵循上述约定,为正号。
7
(3) 运算符“++”和“--” 运算符“++”和“--”是C语言中经常要用到的运算符,表示给它的操作数加1或减1,例如: a=a+1相当于a++或++a; a=a-1相当于a--或--a;
8
可见,“++”和“--”这两个运算符既可放在操作数之前,又可放在操作数之后,这两种表达形式在这里没有区别,但是当增1和减1运算符在表达式中出现时,这两种写法是有差别的。如果运算符在操作数前面,即++a/--a这种形式,则表达式在引用该操作数之前,先要对该操作数加1或减1运算,如果运算符在操作数之后,即a++/a--这种形式,则先引用该操作数,再对它加1或减1运算。
9
例如: a=10; b=++a; 此时,在第二个表达式中,先计算a自增运算,结果为11,再赋值给b,所以b=11,但换成下面的写法: a=10; b=a++; 对于第二个表达式,是先把a的值赋给b,所以有b=10,再对a进行自增运算,此时 a=11。
10
(4) 运算符的执行优先次序 常用的运算符按图3.1所示的优先次序执行,对优先级别相同的运算符,程序按从左至右的顺序进行,除非用圆括号改变运算符的执行次序。
11
2. 关系运算符 关系运算符中的“关系”一词,是指数值与数值之间的关系,即“比较”关系。既然是比较的关系,因此我们可以得知,比较的结果要么为真,要么为假,所以关系运算与逻辑运算具有相同的结果,“1”表示真,“0”表示假。
12
用算术运算符将数据连接起来的表达式是算术表达式,用逻辑运算符将算术表达式连接起来的表达式是逻辑表达式。同理,用关系运算符将算术或逻辑表达式连接起来的表达式就是关系表达式。如x+y>z+5、a<b!=4、x+y==6等都是关系表达式。 表3.2列出了C语言的关系运算符。 前4种运算符的优先级相同,并高于后两种运算符的优先级。 实际编程时常用到算术表达式、关系表达式、逻辑表达式的混合运算,这时就要分清各种表达式的优先级,以便正确使用。
13
关于优先次序,一般遵循以下原则: ① 逻辑非
关于优先次序,一般遵循以下原则: ① 逻辑非!的优先级高于算术运算符。 ② 算术运算符的优先级高于关系运算符。 ③ 关系运算符的优先级高于除逻辑非以外的所有逻辑运算符。 ④ 关系运算符的优先级高于赋值运算符。
14
例3. 2 多运算符的优先级。 #include <stdio
例3.2 多运算符的优先级。 #include <stdio.h> void main( ) { int a=-1,b=1,k; k=(a+1<0)&&!(b-1); printf(“k=%d”,k); } 程序运行结果为:k=0
15
3. 逻辑运算符 逻辑运算符是对逻辑值进行计算的运算符。C语言中逻辑运算符有三个: !:逻辑非,单目运算符; &&:逻辑与,双目运算符; ||:逻辑或,双目运算符。
16
其中,运算符逻辑非“!”的优先级最高,逻辑与“&&”的优先级高于逻辑或“||”。逻辑运算的结果只有两个,要么为真(true),要么为假(false),而C语言认为所有假的结果其值均为0,而所有真的结果其值均为非0,那么只要是非0的数据都将被认为是真的结果, 如-8、9等都将被认为是真。
17
例3.3 #include <stdio.h> void main( ) { int x=-2; if (x) printf(“true”); else printf(“false”); } 输出结果为:true 表3.3列出了逻辑运算符的真值表,逻辑值用1表示真值,用0表示假值。
18
表3.3 逻辑运算符真值表 由此我们可以得出如下结论: 只有当两个操作数同时为真时,逻辑与“&&”的值才为真,有任意一个为假时结果为假。当两个操作数中有任意一个为真时,逻辑或“||”的值就为真,两个操作数同时为假时,结果为假。
19
在逻辑表达式的求解过程中,不一定每个逻辑运算符都被执行,这一点与其他高级语言不同。如果在求解某一个逻辑表达式过程中,根据目前已知条件不必继续往下运算,就可得到正确结果,那么C语言是不会继续执行后面的表达式的。这样做的好处是节省大量时间资源,提高程序运行效率。分析下面的例题:
20
例3.4 逻辑表达式的求解。 第一种情况: #include <stdio.h> void main( ) { int x=0,y=1,z; z=!x||--y; printf(″x=%d,y=%d,z=%d″,x,y,z); } 程序运行结果为:x=0,y=1,z=1
21
第二种情况: #include <stdio. h> void main( ) { int x=5,y=1,z; z=
第二种情况: #include <stdio.h> void main( ) { int x=5,y=1,z; z=!x||--y; printf(“x=%d,y=%d,z=%d”,x,y,z); } 程序运行结果为:x=5,y=0,z=0
22
分析:表达式. x||--y的含义是先求变量x取非的结果和变量y自减后的结果,对这两个结果进行逻辑或运算。第一种情况有x=0,所以
分析:表达式!x||--y的含义是先求变量x取非的结果和变量y自减后的结果,对这两个结果进行逻辑或运算。第一种情况有x=0,所以!x=1,根据表3.2可知,当两个操作数中有任意一个为真时,逻辑或“||”的结果就为真,因此后面的表达式--y根本不需要计算,就可直接得出整个逻辑表达式的最终结果为z=1。但是,如果将x的初值改为一个非零的值,如在第二种情况中被赋值x=5,那么通过计算!x=0的结果不能马上得出整个逻辑表达式的最终结果,还要计算变量y自减后的结果才能做出判断。
23
由此我们可看出,同一逻辑表达式由于被赋予的初值不同,将导致表达式可能被部分执行,同时导致最终结果不同。 同理,对逻辑与运算如a&&b,当操作数a为零时即可得出表达式的结果为0,而不需继续计算b。当遇到逻辑与和逻辑或的混合运算时,就要充分考虑各种运算符的优先级、结合性和可能忽略不计算的表达式。
24
4. 条件运算符(? :) 条件运算符是C语言中惟一的一个三目运算符,由“?”和“:”构成,一般形式为: 表达式1 ? 表达式2 : 表达式3 首先计算表达式1的值并判断,如果为非0,则整个表达式的值为表达式2的值,否则表达式的值为表达式3的值。
25
例3. 5 条件运算符。 #include <stdio
例3.5 条件运算符。 #include <stdio.h> void main( ) { int x=50,y; y=x>70 ? 10 : 20; printf(“y=%d”,y); } 程序运行结果为:y=20
26
条件表达式通常还可以写成后面将学到的if…else语句,上面的例子可以写成: x=50; if(x>70) y=10; else y=20; 在条件表达式中,首先判断表达式1,得到50不大于70的结论,即表达式1值为假,然后再取表达式3的值作为整个表达式的结果,所以有y=20。
27
5. 赋值运算符 赋值运算符用来构成赋值语句,一般形式为: 变量名=表达式; C语言中的赋值运算符有两种:一种是简单的赋值运算符“=”,还有一种是带有运算赋值的运算符,即复合赋值运算符。
28
(1) 简单赋值运算符(=) 例如: int x; x=9; 表示定义了一个整型变量x,并给它赋值为9。 (2) 复合赋值运算符 此类运算符是双目运算符,由某些运算符与赋值符号一起组成复合赋值运算符。共有以下10种: + =、- =、* =、/ =、% =、<< =、>> =、& =、^ =、| =
29
例如: x+=3;. (等价于x=x+3) y. =10;. (等价于y=y. 10) z%=5;
例如: x+=3; (等价于x=x+3) y*=10; (等价于y=y*10) z%=5; (等价于z=z%5) 复合赋值运算符可以理解为先进行赋值号右边的算术运算后,再给左侧变量赋值。 初学者在学习使用复合赋值运算符时可能有些不习惯,但是这种表达方式比较简练,能使编译程序生成质量较高的目标代码。
30
6. 逗号运算符 逗号运算符的优先级是所有运算符中最低的,逗号运算符用于将多个表达式串在一起,最后一个表达式的值和类型决定了整个表达式的值和类型。例如: int a,b; a=(b=4,b+1); 首先进行b=4的赋值运算,再计算b+1的结果为5,最后将5作为整个表达式的结果赋值给a。
31
实质上,逗号运算符使一系列运算从左至右逐个进行,并将最后一个表达式的值和类型作为整个表达式的值和类型。 需要注意的是,并不是所有出现在程序中的逗号都是逗号运算符,比如函数的多个参数之间的逗号是分隔符,变量列表中多个变量之间的逗号也是分隔符而不是运算符。
32
7. 地址运算符 C语言中涉及地址运算时常用到&和*两个地址运算符。虽然在前面我们曾讲过&和*,但那时它们是双目运算符,分别表示位逻辑与和算术乘法符号,在地址运算中,它们是单目运算符,并常用在涉及指针的场合。指针是某一个变量的内存地址,指针变量是专门用来存放变量地址的变量,该指针指向它所定义的数据类型。
33
8. sizeof运算符 sizeof 是一个单目运算符,它返回表达式或括号中的类型说明符的字节长度,它有两种使用形式: sizeof(<类型说明符>) sizeof(<表达式>); 例3.6 sizeof的用法。 #include <stdio.h> void main( ) { float q; printf(“%d\n”,sizeof(q)); printf(“%d\n”,sizeof(int)); } 程序运行结果为: 4 4
34
使用sizeof的目的是增强程序的可移植性,使之不受制于一种计算机固有的数据类型长度。例如,某个数据库程序需要在每个记录中存储多个浮点型数值,为了使此程序能在多种不同字长的计算机上运行,就不能假定每个浮点型数值的定长都是4个字节,合理的使用sizeof将使程序的可移植性大大提高。初学者往往被sizeof()的形式迷惑,认为sizeof()是一个函数,这是不对的,它应是一个运算符。
35
3.2 表达式与表达式的计算 运算符和操作符构成了表达式。在C语言的编程实践中,我们要实现的算法经常要通过多种表达式来表达实现,而在表达式的求解过程中,会遇到各种运算符,因为运算符有其特定的运算规律,因此我们要注意C语言的表达式在某些方面的特殊性。
36
3.2.1 表达式 表达式是由操作数和运算符构成的式子。其中操作数包括常量、变量、函数以及其他一些命名的标识符。
表达式 表达式是由操作数和运算符构成的式子。其中操作数包括常量、变量、函数以及其他一些命名的标识符。 C语言中常见的表达式有: ① 算术表达式:a*3+b/3+x%6 ② 逻辑表达式:x&&!y¦7 ③ 关系表达式:a>(3+x)/5 ④ 条件表达式:y?3:5 ⑤ 赋值表达式:m=7 ⑥ 逗号表达式:a=1,b=2,c+=10
37
C语言表达式与数学表达式在写法上是不一样的,在写C语言表达式时,要注意以下几点:
① 表达式必须以线性形式书写。所有的分子、分母、上标等都应在同一行上。 ② 必须使用合法标识符。 ③ 可以用圆括号“( )”指定运算次序。 ④ 乘法符号必须用“*”,并要明确指出,不可省略。 ⑤ 函数的参数都应写在圆括号“( )”内,标准函数应书写正确。 ⑥ 所有表达式都要遵守自己的运算规则及结合性。
38
表3.4 C语言表达式和数学表达式的对比
39
复合表达式的计算 在实际应用中,我们常常会遇到一个表达式中包含多种不同的运算符,而这些运算符又有不同的优先级和不同的结合性,这些比较复杂的表达式我们称之为复合表达式。对复合表达式的计算,我们就需要充分考虑不同运算符的优先级和结合性,只有这样我们才能得到表达式的正确值并确定最终结果的类型。 表3.5列出了C语言运算符的优先次序。
40
运算符的优先级 每一种运算符都有优先级别,优先级是决定运算符在表达式中的运算次序的一个因素,表达式计算按从高到低的优先级顺序执行,在优先级相同的情况下,由结合性决定计算顺序。 例如: z–y+w*x 乘除优先级高于加减运算的优先级,因此先计算w*x,相当于z–y+(w*x)。在优先级相同的情况下,由加、减法的结合性我们知道是从左至右,先算z–y,再算后面的加法。
41
又如: x=y>2. 6:5 这里用到三个运算符:关系运算符“>”、条件运算符“. :”和赋值运算符“=”,根据表3
42
(2) 运算符的结合性 运算符的结合性是决定运算次序的另一个因素。在运算符优先级相同的情况下,表达式的计算顺序由结合性确定。 C语言中运算符的结合性按结合方向分成从左至右的左结合性和从右至左的右结合性两大类。通常我们用到的+、–、*、/等双目运算符都是左结合的,这种顺序非常符合我们的计算习惯,但还有一些单目、三目的运算符(如赋值)是右结合性。
43
例如: x+y–z 按从左至右的顺序依次求出x+y的值,再减去z,从而得到结果。 a=7 按从右至左的顺序,把数值7向左赋值给a。 在第2章讲述printf()函数时,曾经介绍过,printf()函数的运算次序是自右至左,这一点其实也是printf()函数参数列表具有的右结合性。
44
另外,还有一些运算符的结合性在某些表达式中显得稍微复杂一些。例如: int i=3,j; j=(i++)+(i++)+(i++); 执行后,变量i的值为6,j的值是9。
正确的运算次序为:先把3个还没有进行自增运算的i值相加并送给j后,i再自增3次,而不可以理解为第一次i自增变为4,第二次i自增变为5,第三次i自增变为6,然后相加结果j=4+5+6=15,显然这一结果与实际运行结果不符。
45
如果上式改写成: int i=3,j; j=(++i)+(++i)+(++i); 执行后,结果为i=6,j=18。
和上一种写法不同的是,这里把自增运算符放在变量i的前面,考虑到运算符++的右结合性,正确的运算次序为:先将变量i自增3次,即:i= =6,再把自增以后的i值相加的结果送给j,所以j=6+6+6=18。
46
有时我们还会见到这种情况:x=i+++i+j,那么如何理解该表达式呢?
是把它理解成为x=(i++)+i+j,还是理解成 j=i+(++i)+j呢? C语言编译系统在分析表达式时,会尽量的将若干个字符组成一个运算符,因此应理解成x=(i++)+i+j而非x=i+(++i)+j。来看下面的例题:
47
例3.7 自增运算符的结合性。 #include <stdio.h> void main( ) {int i=3,j=4,x; x=i+++i+j; /* 可理解为x=(i++)+i+j */ printf(“x=%d,i=%d”,x,i); } 程序运行结果为:x=10,i=4。
48
程序运行时,先用没有自增之前的i=3的值来计算x=3+3+4=10,然后再给i自增变为4。
书写程序时应尽量易读,因此为了避免误会,我们应写成x=(i++)+i+j的形式。
49
数据类型转换 我们在编程时,常会遇到一个表达式中既有整类型数据,又有实型数据的情况,当不同类型的常量和变量在表达式中混合使用时,它们最终将被转换为同一类型。 一般来说,类型自动转换时是“向上”靠拢的,表达式的最终类型是表达式中所占字节数最多的类型。C语言中的数据类型转换分为两种:隐式转换和强制转换。
50
隐式数据类型转换 隐式数据类型转换是指当在同一表达式中出现字符型、整型、浮点型数据混合使用时,C语言编译程序按照一定的规则自动转换数据类型,而不是编程者用明确的类型说明符加以说明。例如: int x; float y; 如果要计算x*y,结果的数据类型将是float型,C语言通过隐式数据类型转换,将x自动转换成float型,这种转换对用户来说是隐式的,用户没有明显的感觉,也不需特别声明。
51
隐式转换规则如下: ① 所有短整型和字符型数据都被自动转换成整型。 ② 对于一个表达式中的所有双目运算符的操作数对,若其中一个操作数为long double型,则另外一个操作数也被转换成long double型; 否则,如果一个操作数为double型,则另外一个操作数也被转换成double型; 否则,如果一个操作数为long型,则另外一个操作数也被转换成long型; 否则,如果一个操作数为unsigned型,则另外一个操作数也被转换成unsigned型。
52
除非有强制类型转换,在任意一个表达式中,不同类型的数值型数据按照上述规则实现数据类型的转换以后,每对操作数将都变成相同的类型,整个表达式的结果类型也可以得出,肯定是表达式中占字节数最多的数据类型。 上述数据类型转换可用图3.3表示。
53
假设有按下列方式定义的变量: int i; char ch; float x; double y;
54
请读者考虑表达式(i*ch)+(x/y)-(y–ch)的结果是什么数据类型? 分析:看到这个表达式,首先要根据运算符确定运算次序,有括号的先算括号里的内容,无论是括号内还是括号外的子表达式,都要反复运用上述①、②的规则,如图3.4所示。 图3.4 隐式数据类型转换举例
55
(2) 强制类型转换 和隐式类型转换不同,强制类型转换需要编程者在表达式中明确说明要将某一类型的数据强制转换成指定的数据类型,其一般形式为: (类型名)(表达式) 例如:表达式(int)3.234把3.234转换成整数3,表达式(double)(10%3)把10%3所得结果3转换成双精度数3.0。但是,经过强制类型转换的表达式,不影响原来变量本身的数据类型。
56
例3.8 强制类型转换举例。 #include “stdio.h” void main( ) { int c; c=10; printf(“%f,%d”,(float)(c*0.08),c); } 程序运行结果为: ,10
57
通过运行结果可见,(float)(c. 08)的结果是0
通过运行结果可见,(float)(c*0.08)的结果是 ,而c的值还是10,虽然c参加了浮点运算,但并没有改变c本身的值。 通过强制类型转换,我们可以使整型数据参加浮点运算,类似的应用实例还有很多,在此不再一一叙述,今后大家在编程时将会用到。
58
学习强制类型转换时还应注意以下两点: ① 明确说明的强制类型转换可能会导致数据精度受损失。因为可能出现由长字节的数据类型转换成短字节数据类型所导致的精度损失。 ② 明确说明的强制类型转换不会影响该变量原有的数据类型。如例3.8中的表达式(float)(c*0.08)不会改变变量c的数据类型,仍为int型。
59
除了在表达式中明确说明强制类型转换外,还有一种类型转换,虽然没有明确说明将要转换成的类型,但通过赋值语句和函数返回值两种形式也可实现强制类型转换,这种类型转换是隐式的、不明确的类型转换。看下面的例题:
60
例3.9 强制类型转换举例。 #include <stdio.h> void main( ) {int x,p=5; x=p* ; printf(“x=%d”,x); } 程序运行结果为:x=22
61
分析: 变量p是整型数据,在参加表达式p* 的运算时,先要做隐式数据类型转换,使p变为浮点型参与运算。在得到结果后向x赋值时,又要转换成整型,此时的类型转换虽然也是强制的,但因为没有明确的类型说明符,所以我们称之为隐式强制类型转换。类似的,在有返回值的函数调用中,当函数的返回值类型与函数类型不一致时,将把返回值类型强制转换成函数的类型,这种转换也属于隐式强制类型转换,这一点我们将在今后的学习中详细讲解。
62
本 章 小 结 在这一章里,我们详细介绍了C语言的各种运算符及其分类,以及由这些运算符和操作数构成的表达式的运算和分类。要求大家熟练掌握各种运算符的运算规律和结合方向,尤其是运算符的右结合性,这与我们平时习惯的计算顺序不同,在表达式计算时要充分考虑这些因素。表达式的计算涉及数据类型转换,本章通过几个例题说明了数据类型转换的几种形式,熟练掌握数据类型转换的规则,将有助于我们正确分析、处理程序的运行结果。
63
习题3 一、选择题 3.1 C语言中运算对象必须是整型的运算符是 A) % B) / C) ! D) * 3.2 若变量已正确定义并赋值,符合C语言语法的表达式是 A) a=a+7; B)a=7+b+c,a C)int(12.3%4) D)a=a+7=c+b 3.3 若a、b、c、d都是int型变量且初值为0,以下选项中不正确的赋值语句是 A)a=b=c=100; B)d++; C)c+b; D)d=(c=22)-(b++); 3.4 下列选项中不是C语句的是 A){int i;i++;printf("%d\n", i);} B); C)a=5,c= D){;} 3.5 合法的C语言赋值语句是 A)A=B= B)K=int(a+b) C)a=58,b= D)—i;
64
3.6 若变量a、b、t已正确定义,要将a和b中的数进行交换,以下选项中不正确的是 A) a=a+b,b=a-b,a=a-b; B)t=a;a=b;b=t; C) a=t;t=b;b=a; D)t=b;b=a;a=t; 3.7 若有正确定义语句: double x= ;语句printf("%f\n",(int)(x* )/(double)1000);的输出结果是 A)输出格式不对 B) C) D) 若有以下程序段 int c1=1,c2=2,c3; c3=c1/c2; printf("%d\n",c3); 执行后的输出结果是: A) B)1/ C) D)1 3.9若有以下程序段 int a=0,b=0,c=0; c=(a-=a-5),(a=b,b+3);; printf("%d,%d,%d\n",a,b,c); 执行后的输出结果是: A) 3,0, B)0,0, C)-10,3, D)3,0,3
65
二、填空题 3.10 表达式3.5+1/2的计算结果是______________。 3.11 对数学式,写出三个等价的C语言表达式______、______、______。 3.12 表达式a=10应当读作____________________。 2.13 若k为int型变量且赋值11,请写出运算k++后表达式的值___和变量k的值___。 2.14 若x为double型变量,请写出运算x=3.2,++x后表达式的值___和变量x的值___。
66
三、编程题 3.15 编写程序,把560分钟换算成用小时和分钟显示,然后输出。 3.16 编写程序,输入两个整数:1500和350,求它们的商和余数并输出。 3.17 编写程序,读入三个双精度的数,求它们的平均值并保留小时点后一位,对小数点后第二位进行四舍五入,并输出。 3.18 编写程序,读入三个整数给a、b、c然后交换它们中的数,交换原则:a给b、b给c、c给a,然后输出a、b、c。
Similar presentations