第3章运算符与表达式.docx
- 文档编号:30645737
- 上传时间:2023-08-18
- 格式:DOCX
- 页数:38
- 大小:184.66KB
第3章运算符与表达式.docx
《第3章运算符与表达式.docx》由会员分享,可在线阅读,更多相关《第3章运算符与表达式.docx(38页珍藏版)》请在冰豆网上搜索。
第3章运算符与表达式
第3章运算符与表达式
运算符和表达式是C语言的核心语法,C语言运算符丰富,不仅有优先级概念,还有结合性的概念;不仅有算术、关系和逻辑运算符,还有赋值、逗号运算符。
由于C语言运算符丰富,因此表达式类型多,而且将字符、逻辑值数值化,使得C语言表达式的使用变得非常灵活。
熟练掌握C语言的运算符和表达式是学好C语言的基本要求。
3.1运算符与表达式概述
3.1.1C运算符简介
运算是对数据的加工过程,表示不同运算的符号叫做运算符,而参与运算的数据叫做操作数。
不同的计算机语言有不同的运算符集。
C语言中提供了丰富的运算符,它除了常规运算符如算术运算符、关系运算符、逻辑运算符外,还有如赋值运算符、逗号运算符、位处理运算符、指针运算符、访问结构体和联合体成员运算符等C语言所特有的运算符。
在C语言中,除了几个控制语句外,几乎所有的操作都是通过由运算符构造的表达式来完成,因此C语言运算符的作用范围很广。
1.运算符分类及属性
C语言的运算符可分为如表3-1所示的15类。
每个运算符除了优先级外,还有结合性等属性。
一个表达式的计算顺序需要将运算符的优先级和结合性综合起来考虑。
表3-1C语言运算符的优先级及结合性
优先级
运算符
含义
操作数个数
结合方向
举例
1
()
[]
->
.
圆括号
下标运算符
指向结构体成员
结构体成员
自左至右
(a+b)*c
array[5]
p->num
stud.name
2
!
~
++
--
-
(类型)
*
&
sizeof
逻辑非
按位取反
自增
自减
负号
类型转换
间接访问
取地址(取指针)
变量或类型的长度
1
单目运算符
自右至左
!
a
~0
(i++)+(++i)
(i--)+(--i)
-x
(float)n/20
x=*p
p=&x
sizeof(long) 3 * / % 乘法 除法 求余 2 双目运算符 自左至右 a*b a/b 25%3 4 + - 加法 减法 2 双目运算符 自左至右 a+b a-b 5 << >> 左移 右移 2 双目运算符 自左至右 a<<3 a>>2 续表 优先级 运算符 含义 操作数个数 结合方向 举例 6 < <= > >= 小于 小于等于 大于 大于等于 2 双目运算符 自左至右 if(x 其余运算符类似 7 == ! = 等于 不等于 2 双目运算符 自左至右 if(x==y) while(i! =0) 8 & 位与 2 双目运算符 自左至右 a&b 9 ^ 位异或 2 双目运算符 自左至右 b^024 10 | 位或 2 双目运算符 自左至右 044|c 11 && 逻辑与 2 双目运算符 自左至右 (a>b)&&(c 12 || 逻辑或 2 双目运算符 自左至右 (x>1)&&(y<3) 13 ? : 条件 3 三目运算符 自右至左 x=a>b? a: b 14 = +=-= *=/= %=&= ^=|= >>=<<= 赋值 (复合赋值) 2 双目运算符 自右至左 a=a+b a+=b(同a=a+b) a*=b+c(同a=a*(b+c)) a&=b(同a=a&b) 其余复合运算符类似 15 , 逗号 2 双目运算符 自左至右 a=1,b=2,c=12 本章将详细讨论C语言的算术运算符、赋值运算符、强制类型转换运算符、逗号运算符、关系运算符、逻辑运算符。 其余的运算符将在后续章节中逐步介绍。 2.关于运算符的几点说明 (1)C语言的运算符按其性质分类有15类;按运算符所要求操作数的个数分类,又可以分为单目运算符、双目运算符和三目运算符。 例如: “++”为单目运算符,“*”、“/”为双目运算符,“? : ”为三目运算符。 (2)优先级别。 一个表达式中可以有多个运算符,这时首先按运算符的优先级别进行运算,C语言运算符的优先级别与数学运算中的意义相同,它决定了一个表达式的运算顺序。 如果一个操作数两侧有两个不同优先级别的运算符,则先执行优先级别高的运算。 如4-9*7,在9的两侧分别为-、*,根据C运算符的运算级别,则先*后-。 (3)结合性。 如果一个操作数的两侧有两个优先级别相同的运算符,则按结合方向顺序进行处理。 C语言运算符的结合性分为: ①左结合性。 如果一个运算符对其操作数自左至右执行规定的运算,则称该运算符是左结合的。 运算符+、-、*、/、%、&&、||等都是左结合性的运算符。 例如: 5*8/3 8两侧的运算符分别为*、/,它们的优先级相同,根据“自左至右”方向的结合原则先*后/,即8先和其左边的运算符结合(5*8),再与其右边的运算符结合(40/3)。 ②右结合性。 如果一个运算符对其操作数自右向左执行规定的运算,则称该运算符是右结合的。 运算符=、! 、++、--等都是右结合性的运算符。 例如: a=b=c=8 b两侧都是赋值运算符“=”(即优先级相同),根据“自右至左”方向的结合原则,它先与其右侧的赋值运算符结合,即a=(b=c=8)。 由于赋值运算符“=”是一个双目运算符,因此b右侧赋值运算符的右边要求有一个操作数,这里是“c=8”,那么是把c的值直接赋给b呢? 还是先进行“c=8”运算呢? 由于c两侧的运算符级别相同且是“右结合性”,因此c应先与其右的赋值运算符结合,故表达式相当于a=(b=(c=8))。 关于“结合性”的概念是其他高级语言没有的,是C语言的特点之一。 (4)在使用C语言的运算符时,应注意运算符对操作数类型的要求。 如+、-、*、/的运算对象可以是整型或实型数据,而运算符%(求余运算符)要求参加运算的两个操作数都必须是整型数据。 (5)一个运算符两侧的操作数类型可以不同,C编译会自动进行类型转换,使二者具有同一种类型,然后进行运算。 (6)C语言的运算符较多,又有其优先级和结合性,初学者要特别仔细,善于归纳。 3.1.2C表达式简介 用运算符将常量、变量、函数等(称为操作数)连接起来的符合C语言规定的式子称为C语言表达式。 作为表达式的特例,一个单独的变量或常量也可以称为一个表达式。 下面是几个表达式的例子: a+b-c*3+d/ea>>8y=a+b||ca+=a+b C语言中所有的运算都是用表达式表示的,清楚地了解表达式的求值顺序是正确书写表达式的关键。 表达式的求值顺序取决于表达式中参与运算的运算符的优先级、结合性和语言的具体实现。 例如,表达式: a+b*c 因“+”的优先级低于“*”,所以运算顺序为先计算b*c的值,然后再与a相加。 再如,表达式: -n++ 由于运算符“-”(负号运算符)和“++”的优先级相同,结合性均为右结合的,所以求解该表达式的顺序为: 先进行n++运算,然后再对运算结果求负。 即相当于: -(n++)。 3.2算术运算符和算术表达式 算术运算是我们日常生活中使用最为常见的一种运算。 C语言不仅提供了基本算术运算符,如加、减、乘、除和求余等,还提供了增量、减量运算符。 3.2.1基本算术运算符和算术表达式 1.基本算术运算符 C语言中的基本算术运算符包括单目算术运算符: -(负号) 和双目算术运算符: +(加)-(减)*(乘)/(除)%(模除) 单目运算符“-”又叫做一元减运算符,其作用与数学中的负号相同,即取操作数的负值。 双目算术运算符+、-与数学中的加、减的作用相同,而*、/则分别对应数学中的乘、除。 例如,3+5,6-4,3*a,b/c等。 双目运算符%叫做模运算符或称为求余运算符,其作用是取被除数的模,即被除数除以除数后的余数。 例如,13%5的结果为3,3%5的结果也为3,而-13%5的结果为-3。 单目算术运算符“-”结合方向为“自右至左”,其优先级别高于双目算术运算符,双目算术运算符的*、/、%的优先级别相同,运算符+、-的优先级别相同,但前者的优先级别高于后者。 双目算术运算符的结合方向为“自左至右”。 需要说明的是: (1)运算符“%”要求它的两个操作数都是整型数据。 (2)其他运算符可以是任何基本数据类型。 (3)若运算符“/”的两个操作数都为整数,则运算结果即商也为整数,小数部分被自然舍弃了。 例如: 13/5的运算结果为2,5/13的运算结果为0。 参加运算的两个数只要有一个为实型,则结果是实型。 2.算术表达式 用算术运算符和括号将运算对象(也称操作数)连接起来的、符合C语言语法规则的式子称为C算术表达式。 运算对象包括常量、变量、函数等。 例如,下面是一个合法的C算术表达式: a*b/c-1.5+a C语言规定了运算符的优先级和结合性。 在写C表达式时一定要注意运算符的优先次序和结合方向,C表达式求值时,先按优先级别高低次序执行。 如果在一个运算对象两侧的运算符的优先级别相同,则按规定的“结合方向”处理。 如: a-b+c*d 该表达式的运算顺序为: (1)由于b两侧的运算符的优先级别相同,则按“自左至右”结合方向进行,因此b先与减号结合,执行(a-b)运算; (2)由于c两侧运算符的优先级不同,*高于+,因此c先与*结合,执行(c*d)运算; (3)最后执行(a-b)+(c*d)运算。 3.2.2增量减量运算符 C语言提供了两个使变量的值增1、减1的运算符: ++(增量)--(减量) 它们都是单目运算符,其运算结果是将操作数的值加1、减1。 例如: 设变量i的值为5,则++i后i的值将变为6,即表达式++i相当于赋值表达式i=i+1。 又如--i相当于赋值表达式i=i-1。 由于增量、减量运算符本身就隐含有赋值操作,所以它们的操作数必须是一个变量。 例如: 5++或(a+b)++都是不合法的。 但是,与其他单目运算符不同的是,这两个单目运算符的操作数既可以放在运算符的前面,又可以放在运算符的后边。 如: ++i(前置增量)i++(后置增量) --i(前置减量)i--(后置减量) 作为一个单独的表达式时,运算符前置和后置是没有什么区别的,但在一个还包含有其他运算的表达式中,运算符前置和后置却会产生不同的效果。 在一个包含有其他运算的表达式中,增量或减量运算符的前置意味着先对其操作数进行相应的增量或减量,然后再进行其他的运算;而后置增量或后置减量运算符先对其操作数进行其他运算,然后对操作数进行相应的增量或减量运算。 例3.1测试表达式中前置、后置增量运算符的运算顺序。 #include voidmain(){ inti=5,j,k; j=++i; printf(i=%d,j=%d,i,j); k=i++; printf(i=%d,k=%d\n,i,k); } 运算结果为: i=6,j=6,i=7,k=6 表达式使用中应注意的问题: (1)++和--的结合方向是“自右至左”,即右结合性。 如果有-i++,i的左边是负号运算符,右边是增量运算符。 因负号运算符与增量运算符同优先级,而它们的结合方向为“自右至左”,故先进行“i++”运算,再进行负号运算,即它相当于-(i++)。 例如,如果i的值为3,对于printf(%d,-i++);语句,则先取出i的值使用,输出-i的值-3,然后使i增值为4。 另外,假若按左结合性,则上式相当于(-i)++,而(-i)++是不合法的。 (2)增量、减量运算符常用于循环语句中,使循环变量自动加1或减1。 也用于指针变量,使指针指向下一个或上一个存储单元。 这些将在后续章节中介绍。 (3)在一个表达式中含有多个增量(减量)时,系统在计算表达式前,先对表达式中的增量(减量)进行扫描,并对其前置变量逐一增1(减1),然后计算表达式的值,最后再对后置变量增1(减1)操作。 例3.2多个增量(减量)运算符组成的表达式的运算顺序。 #include voidmain(){ inti=3,k; k=(i++)+(i++)+(i++); printf(i=%d,k=%d,i,k); i=3; k=(++i)+(++i)+(++i); printf(i=%d,k=%d,i,k); i=3; k=(++i)+(i++)+(++i); printf(i=%d,k=%d\n,i,k); } 运行结果为: i=6,k=9i=6,k=18i=6,k=15 结果分析: 首先分析表达式(i++)+(i++)+(i++)的值。 有人认为它相当于3+4+5,即12。 事实上,用TurboC系统时它等于9。 它是先把i的原值(3)取出来参加表达式的运算,因此先进行三个i相加得9;然后再使变量i进行三次增1,故i的值变为6。 分析表达式(++i)+(++i)+(++i)的值。 有人认为它相当于4+5+6,即15。 事实上,它的值是18。 原因是: ++i的自增1是在整个表达式计算之前就进行的,即对表达式扫描,先对i进行三次增1,i的值变为6;然后进行6+6+6的运算,故得18。 分析表达式(++i)+(i++)+(++i)的值。 系统先对表达式扫描,遇到两次++i和一次i++,先对i进行两次增1,i的值变为5;然后进行5+5+5的运算,故得15;运算完后再使变量i增1(因为i++是先引用后增1)。 (4)C语言中的运算符有的是一个字符,有的是由两个字符构成,在表达式中如何组合呢? 如i+++j是理解为(i++)+j呢? 还是i+(++j)呢? C语言在编译处理时尽可能多地“自左至右”将若干个字符组成一个运算符(在处理标识符时也按同一原则处理),如i+++j,将解释为(i++)+j,而不是i+(++j)。 (5)含有增量(减量)的表达式作为函数的参数时,表达式的求值顺序与上面介绍的有所不同。 例如,TurboC环境下,如果i的初值为3,则: printf(%d,(++i)+(++i)+(i++)); 的输出结果为14。 这是因为: 对函数参数求值时并不是先扫描后求值,它首先求第一个++i的值为4,再求第二个++i的值为5(因求第一个++i后i的值已为4),再求第三个i++的值为5(因求第二个++i后i的值已为5),最后将它们的和4+5+5等于14输出。 (6)C语言中类似上述的问题还有一些。 例如,i的初值为3,如果有下面函数的调用: printf(%d,%d,i,i++); 在有的系统中,对函数的参数是从左至右求值,输出“3,3”。 在多数系统中,对函数参数的求值顺序是自右至左(TurboC就是如此)。 上面的printf函数是要输出两个表达式的值(i和i++分别是两个表达式),先计算出i++的值再计算i的值,i++的值为3,计算i++后i的值变为4,这个4就成了第一个参数i的值,因此上面函数输出的结果是“4,3”。 对于上述问题,读者不必死记,因不同系统的处理方法不一样。 但应当知道使用C语言可能出问题的地方,必要时上机试一下,以免遇到问题时不知其所以然。 使用++和--运算符能给编程带来方便,使程序更加简洁,但也会出现一些人们“想不到”的副作用,初学者要慎用。 3.3赋值运算符和赋值表达式 在程序中使用最频繁的表达式就是赋值表达式,在C语言中,赋值的方法灵活多变。 本节介绍C语言的赋值运算符和赋值表达式及表达式中类型的转换。 3.3.1赋值运算符和赋值表达式 1.赋值运算符 “=”为C语言的赋值运算符。 赋值运算符的运算级别仅高于逗号运算符“,”,低于其他所有的运算符。 它的结合方向是自右至左。 运算符“=”属于双目运算符,其左边的操作数称为赋值运算符的左值,其左值只能是一个变量;右操作数可以是任意表达式。 2.赋值表达式 由赋值运算符将一个变量和一个表达式连接起来的式子称为“赋值表达式”。 例如“a=5+3”就是一个赋值表达式,它的作用是执行一次赋值操作(或称赋值运算),把表达式5+3的结果赋给变量a。 赋值表达式的一般形式如下: 左值=右值 (1)左值(leftvalue,缩写lvalue)是能出现在赋值表达式左边的表达式。 左值表达式具有存放数据的空间,并且存放是允许的。 例如: inta=10;//a是变量,所以它是正确的左值 a+b=20;//错误,a+b是一个表达式,没有对应的存储单元,它不能作为左值 (2)右值(rightvalue,缩写rvalue)是可以出现在赋值表达式右边的表达式。 右值表达式可以是任意类型的表达式。 例如: intx,y=8,z; x=y; z=(z+y)*5-x*x; 赋值表达式的作用就是将赋值运算符的右值(即右值表达式的运算结果)赋给左值变量,该值同时又是整个赋值表达式的值。 如赋值表达式a=6/3是把右值表达式6/3的值2赋给左值a,同时整个赋值表达式a=6/3的值也等于2。 由于赋值表达式的右值表达式可以是任意合法的C表达式,而赋值表达式本身就是一个合法的C表达式,所以赋值表达式可以作为赋值表达式的右值。 例如: i=j=5 就是一个合法的赋值表达式,由于赋值运算符具有右结合性,故上述表达式相当于: i=(j=5) 即先把5赋给变量j,然后把表达式j=5的值赋给变量i。 下面是赋值表达式的一些例子: a=b=c=7(表达式的值为7,a、b、c的值均为7) a=5+(c=7)(表达式的值为12,a的值为12,c的值为7) a=(b=6)-(c=4)(表达式的值为2,a的值为2,b的值为6,c的值为4) a=(b=10)/(c=2)(表达式的值为5,a的值为5,b的值为10,c的值为2) 将赋值表达式作为表达式的一种,使赋值操作不仅可以出现在赋值语句中,而且可以以表达式的形式出现在其他语句(如循环语句)中,这是C语言灵活的一种表现。 以后将会看到这种应用的优越性。 3.3.2复合赋值运算符 C语言中的所有双目算术运算符和双目位运算符均可与赋值运算符组合成一个单一的运算符,即复合赋值运算符,一共有十个: +=(加等)-=(减等)*=(乘等)/=(除等)%=(取模等) &=(与等)|=(或等)^=(异或等)<<=(左移等)>>=(右移等) 后五种是有关位运算的,将在第13章中介绍。 复合赋值运算符的优先级别和结合性都与赋值运算符相同。 复合赋值运算符可以用来构成复合赋值表达式,其构成形式与赋值表达式相同。 复合赋值表达式的运算过程为: 先将左值和右值作运算符所规定的算术或位运算,然后将其结果赋给其左值表达式。 下面以*=为例说明复合赋值表达式的运算过程。 设i和j的值分别为5和6,则: i*=i+j 表示先计算复合赋值表达式的右值表达式的值,即算术表达式i+j的值,其运算结果为11;然后把左值和右值作复合赋值运算所规定的算术运算,即i与上述结果的乘积,其运算结果为55;最后进行赋值运算操作,即把上述运算结果赋给运算符的左值i。 同时整个表达式的值亦为55。 从其功能上看i*=i+j等价于i=i*(i+j)。 下面再来看一个赋值表达式包含复合赋值运算符的例子: a*=a-=a+a 也是一个赋值表达式。 如a的初值为2,此赋值表达式的求解过程如下: (1)运算符*=和-=的优先级相同且具有右结合性,故先进行“a-=a+a”运算,相当于a=a-(a+a),它的值为-2; (2)再进行“a*=-2”运算,相当于a=a*(-2)=(-2)*(-2)=4。 注意,此时a*(-2)所用的a的值为-2,而不再是2,这是因为第 (1)步运算已对a进行了重新赋值。 由此可见,表达式a*=a-=a+a相当于: a=a*(a=a-(a+a)) C语言采用这种复合运算符,一是为了简化程序,使程序精练简洁,二是为了提高编译效率。 3.4数据类型的转换 3.4.1隐式类型转换 一般来说,一个双目运算符的两个操作数的类型必须一样才能进行运算操作,但C语言允许在一个表达式中存在不同数据类型的操作数。 在对这样的表达式求值时,编译系统会对其中的一些操作数自动地进行类型转换——称为隐式类型转换,以使一个双目运算符两个操作数的类型一致。 例如: 10+15.2-27*a-b 是合法的。 运算时,不同类型的数据要先转换成同一类型,然后进行运算。 隐式类型转换的规则为: (1)无条件的隐式类型转换。 所有的char型和short型都必须转换为int型,所有的float型都必须转换成double型。 即使两个操作数都是相同类型,也要进行类型转换。 (2)统一类型的隐式类型转换。 如果一个运算符的多个操作数的类型不一致,则需要将较低的类型转换为较高的类型,然后基于同一类型进行运算。 例如,如果类型级别最高的一个操作数为long型,则其他操作数也要转换成long型;如果类型级别最高的一个操作数为double型,则其他操作数也要转换为double型。 隐式转换规则如图3-1所示。 无条件类型转换 图3-1中的横向箭头表示必定转换,而纵向箭头表示涉及不同类型数据之间的运算时的转换方向,即当一个运算符的多个操作数为不同类型时的转换方向。 纵向箭头的方向只表示数据类型的高低,数据类型由低向高转换,并不表示一定要逐级转换,例如一个double型与一个int型数据之间的运算,是直接将int型转换为double型,而不是先由int型转换为long,再由long型转换为double型。 例如: f为float型,表达式: a+b-f 运算次序为: ①进行a+b的运算,先将a、b都转换成int型(无条件转换),转换后分别是int型数据97和98,然后对两个int型数据进行运算,结果为int型数据195; ②进行195-f的运算,需要把int型195和float型f都转换成double型相加(其中,float到double型的转换是无条件转换,int到double型的转换是统一类型转换),其结果为double型。 应当说明的是,类型的转换仅是临时性的,它并不改变变量的数据类型,只是在执行运算时将变量值的类型作了临时的转换。 例3.3隐式类型转换的效果。 #include voidmain(){ inti=3,j; j=i+1.5;/*语句A*/ printf(di=%d,fi=%f\n,j,i+1.5);/*语句B*/ } 运行结果如下: di=4,fi=4.500000 说明: ①“语句A”和“语句B”中的表达式i+1.5运算时,i、1.5都转换成double型,结果为double型(4.50000000000000); ②“语句A”中将double型的i+1.5赋给整型变量j,结果j的值为4(详见3.4.2节的赋值类型转换)。 3.4.2赋值表达式两侧数据的类型转换 赋值表达式的左值是变量,而不同类型变量存储数据的形式是不一样的,因此赋值表达式两侧数据类型的转换规则与数据的存储形式有密切的关系。 1.整型数据与实型数据之间的转换 (1)将实型数据(包括单、双精度)赋给整型变量时舍弃实数的小数部分。 如i为整数变量,执行“i=6.76”时的结果是i的值为6。 (2)将整型数据赋给单、双精度变量时数值不变,但以浮点数形式存储到变量中,如将整数23赋给float型变量f(即f=23),先将整数23转换为浮点数形式,再存储到f中,f
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第3章 运算符与表达式 运算 表达式