资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,#,运算符与表达式,运算符,(operator),也称为操作符,对程序中的数据进行运算。参与运算的数据称为操作数,(operand),。,变量、字面常量等通过运算符组合成表达式,一个表达式也能作为操作数来构成更复杂的表达式。,表达式,(expression),是构成程序语句的基本要素。,对于运算符,应注意以下几方面。,(1),运算符的功能和语义。,(2),运算符的操作数。每个运算符对其操作数的个数、类型和值都有一定限制。,(3),运算符的优先级,(precedence),。每个运算符都有确定的优先级。,(4),运算符的结合性,(associativity),。,1,运算符,说明,示例,+,、,-,正负号,+,、,-,加,减,x=y+z,,,x=y-z,%,取余,x=y%z,*,、,/,乘,除,x=y*z,,,x=y/z,+/-,自增和自减运算符,C+,语言中包含了丰富的运算符,主要有算数运算符、关系运算符、逻辑运算符、赋值运算符和位运算符。下面逐一介绍这些常用的运算符。,1.1,算数运算符,算数运算符主要用于算数运算。,2,2025/4/19 周六,注意:,对于除,若两个操作数都是整数,结果为整数(商部分)。,1/2=0 5/2=2,%通常称为取模运算,两个操作数必须都是整型数,结果为余数,余数的符号与左边数的符号等同。,3%2=1 -3%2=-1 3%-2=1 -3%-2=-1 8%4=0,在算术运算中需要注意溢出问题。,两个整数做加法、减法或乘法运算时,即便结果溢出也不是错误。,例如:,short s1=32765;,s1=s1+3;/,结果是否超过,short,的最大值?,couts1endl;/,输出,-32768,,而不是,32768,。,实际上,观察二进制数据,这两个值是一样的,。,3,2025/4/19 周六,+,和,有前置方式和后置方式。前置方式使操作数增,1,(或减,1,),新值参与表达式的运算;后置方式是在操作数参与表达式运算后其值增,1,(或减,1,)。,例如:,+i,,,-i/,在使用,i,之前,先使,i,的值加,1,,减,1,i+,,,i-/,在使用,i,之后,使,i,的值加,1,,减,1,一元负号运算符对运算对象值取值取负后,返回其(提升后的)副本:,Int i=1024,;,Int k=-i,;,/k,是,-1024,bool b=true;,bool b2=-b;,b2=,?,对大多数运算符来说,布尔类型的运算对象将被提升为,int,型。如上所示,布尔变量,b,的值为真,参与运算时将被提升成整数值,1,,对它求负的结果是,-1,,将,-1,再转回布尔值并将其作为,b2,的初始值,显然这个初始值不等于,0,,转换成布尔值后应该为,1,。所以,,b2,的值是真!,(,false,可以代表,0,,但,true,有很多种,并非只有,1,),4,2025/4/19 周六,1.2,关系运算符,关系运算符主要用于关系比较运算。,关系运算符,名称,效果,a=b,等于,TRUE,,如果,a,等于,b,a!=b,不等于,TRUE,,如果,a,不等于,b,ab,大于,TRUE,,如果,a,大于,b,a=b,大于等于,TRUE,,如果,a,大于或者等于,b,关系运算的结果是一个表示“真”或“假”的逻辑值,即一个,bool,值。当关系成立时,其运算结果为真;当关系不成立时,结果为假。但,C/C+,语言中没有逻辑型,关系运算的结果要用一个,int,值表示,,0,即为假,,1,即为真。,5,2025/4/19 周六,关系运算符的优先级为,(,括弧中运算符的优先级相同,),:,(,、,=,、,、,b=c;/,等价于,d=(a b)=c);d,的值为,0,d=a=b c;/,等价于,d=(a=(b b b)=c);d,的值为,1,可以使用,(),来改变运算符的计算次序。,由于浮点数在计算机内进行运算和存储时会产生误差,因此在比较两个浮点数时,建议不要直接比较两数是否相等。例如,执行下面语句:,double d1=3.3333,d2=4.4444;,if(d1+d2 =7.7777),cout,相等,endl;,else,cout,不等,endl;,coutd1+d22 0,,,ab+c 0,,,a=2 1,,,a=a 0,,,aa 0,,,b=a=2 1,6,2025/4/19 周六,条件语句中用,“,=,”,来判断浮点数是否相等,结果是不等,但,d1+d2,输出结果却是,7.7777,。两个实型数即便输出结果完全一样,其内部值也可能不一样。判断两个实数是否相等的正确方法是:判断两个实数之差的绝对值是否小于一个给定的允许误差数,如判断,d1,是否等于,d2,时,应改为:,fabs(d1+d2-7.7777)=1e-6,其中,,fabs(),是计算绝对值的一个库函数,使用时要包含头文件,math.h,。,7,2025/4/19 周六,1.3,逻辑运算符,对逻辑值进行运算的运算符就是逻辑运算符。,C+,语言提供了,3,个逻辑运算符,用于表示操作数之间的逻辑关系,它们是,!(,逻辑非,),、,&(,逻辑与,),、,|(,逻辑或,),。逻辑运算的结果仍然是逻辑值。,逻辑运算符,名称,效果,!a,求反,TRUE,,如果,a,不为,TRUE,a&b,逻辑与,TRUE,,如果,a,与,b,都为,TRUE,a|b,逻辑或,TRUE,,如果,a,或,b,任一为,TRUE,逻辑非,(!),是单目运算符,它对操作数进行取反运算。当操作数为非,0(,逻辑真,),时,!运算后结果为,0(,逻辑假,),。反之,若操作数为,0(,逻辑假,),,,!,运算后结果为,1(,逻辑真,),逻辑运算符的运算优先级为:,!,高于,&,高于,|,。注意,!的优先级具有较高优先级,甚至高于算术运算符。而,&,和,|,的优先级则比算术运算符和关系运算符低。,8,2025/4/19 周六,因,C+,将逻辑值保存为整数值,这样使得逻辑值可参与所有的运算,而且逻辑运算符可作用于所有类型的值,而没有语法错误提示。这是,C/C+,语法不严密之处。读者应注意避免。,9,2025/4/19 周六,1.4,赋值运算符,赋值运算就是将一个表达式的值赋给一个变量。,C+,语言提供了两类赋值运算符:基本赋值运算符和复合赋值运算符。,基本赋值运算符为,“,=,”,,复合赋值运算符有多种形式:,+=,、,-=,、*,=,、,/=,、,%=,、,=,、,&=,、,=,、,|=,。,(1),赋值运算符都是双目运算符,从右向左进行。例如,,sum1=sum2=0,相当于,sum1=(sum2=0),,先执行,sum2=0,,后执行,sum1=sum2,。,(2),要求赋值运算符左操作数必须是左值,左值能存储值。例如:,x=3+5/,正确,,x,是左值,x-3=5/,语法错误,,x-3,不是左值,(3),复合赋值运算符是将算术运算或位运算与赋值相结合,同一个变量即参加运算,也是被赋值的变量,出现在赋值运算符的两边。复合赋值运算符是一个整体,中间不能用空格隔开。例如:,a*=6/,相当于,a=a*6,a%=6/,相当于,a=a%6,a+=3+6 /,相当于,a=a+(3+6),初学者容易犯的一个错误就是混淆,“,=,”,运算符和,“,=,”,运算符。分析下面代码:,int a=5,b=3;,int d=a=b;/d,的值为,0,10,2025/4/19 周六,赋值运算符,名称,效果,=,赋值,x=10,+=,赋值与和,x+=10,(等于,x=x+10,),-=,赋值与减,x-=10,*=,赋值与乘,x*=10,/=,赋值与除,x/=10,&=,赋值位与,x&=0 x02,|=,赋值位或,x|=0 x02,答案是有的,对于,a=a+1,,表达式,a,被计算了两次,对于复合运算符,a+=1,,表达式,a,仅计算了一次。一般的来说,这种区别对于程序的运行没有多大影响,但是当表达式作为函数的返回值时,函数就被调用了两次,而且如果使用普通的赋值运算符,也会加大程序的开销,使效率降低。,a=a+3;,与,a+=3;,有没有区别?,11,2025/4/19 周六,1.5,位运算符,位运算是指对字节内部的二进制位进行移位或逻辑运算。,位运算是通过位运算符来完成的。,位运算的操作数必须是,char,、,short,、或,int,值,而且结果也是,char,、,short,或,int,值。,除了按位求反,是单目运算符,其余位运算都是双目运算符。,C+,提供了两类位运算:移位运算和按位逻辑运算。,1.5.1.,移位运算符,移位运算符的格式为:,operand n,将操作数,operand,向右移动,n,个二进制位,保持符号不变。,其中,,n,为整数。注意移位运算并不改变,operand,本身的值。例如:,比如,22;,值变成,8,;,对于移位运算,注意以下几点:,(1),不要尝试对,float,或,double,数据进行移位运算,编译会出错。,(2),移动位数,n,应不大于左操作数的位数,如,int,移位应不大于,32,。如果,n,大于左操作数位数,实际移动位数要自动按字长取模:,n%(sizeof(int),。例如,,i33,就是,i,左移,1,位。,(3),左移位,与,cout,与,cin,可能混淆,可用括号消除这些错误,例如,cout(k3),12,2025/4/19 周六,1.5.2,位逻辑运算符,按位逻辑运算有,4,个:求反,、与,&,、或,|,、异或,。,(1),按位求反,“,”,运算符是一个单目运算符,对操作数逐位取反,得到反码。若二进制位为,0,,则取反后为,1,;若二进制位为,1,,则取反后为,0,。,例如:,short int m=0 xc3/,结果为,0 xff3c,运算符具有比较高的优先级,高于一般的算术运算符。而其它按位逻辑运算符的优先级则比较低。,(2),按位逻辑与,“,&,”,对两个操作数逐位进行运算。若对应位都为,1,,则该位结果为,1,,否则为,0,。例如:,0 xc3 0000 0000 1100 0011,0 x6e 0000 0000 0110 1110,0 x42 0000 0000 0100 0010,short int a=0 xc3&0 x6e/,结果为,0 x42,(3),按位逻辑或,“,|,”,对两个操作数逐位进行运算。若对应位都为,0,,则该位结果为,0,,否则为,1,。例如:,short int b=0 x12|0 x3d/,结果为,0 x3f,13,2025/4/19 周六,位运算符,名称,效果,a,按位反,1,,如果,a,不为,1,a&b,按位与,1,,如果,a,与,b,都为,1,a|b,按位或,1,,如果,a,或,b,都不为,1,ab,按位异或,1,如果,a,和,b,不相同,右移,(4),按位逻辑异或,“,”,也是对两个操作数逐位进行运算。异或运算的规则是,若对应位不同,则该位结果为,1,,否则为,0,。例如:,short int c=0 x5a 0 x26/,结果为,0 x7c,按位逻辑异或有一个特点,如果,a b=c,,那么,c b=a,。,b,将,a,转换为,c,,也能将,c,再复原为,a,。显然,两个相等的值异或运算,结果为,0,。不相等的两个值异或运算结果不为,0,。,14,2025/4/19 周六,1.6 new,和,delete,new,和,delete,是,c+,新增的运算符,实现存储的动态分配和释放功能。,new,可以用于动态申请内存空间,为各种数据类型分配内存。用户可以使用,new,申请一段内存空间,如数组、结构体或整型变量等。,new,运算符返回系统分配的内存空间的首地址,需要相应类型的指针保存该地址。,下面,3,种方式都可以正常申请内存空间。,/,定义一个整型指针,Int*p,;,/,定义一个未初始化的,int,型变量,p=new int,;,/,定义一个指针变量,并初始化该内存区域内容为,12,Int*p1=new int,(,12,);,/,定义一个指针变量,指向,10,个整型变量区域。该区域与数组类似,可以像访问数组一样访问该区域,Int*p2=new,int10,;,15,2025/4/19 周六,在使用完毕,new,申请的内存空间后,用户需要释放该内存空间。如果用户只申请内存空间,并不释放,会占用大量的内存空间,使系统运行速度变慢,甚至会造成系统崩溃。因此,在使用完申请的内存空间后,用户需要使用,delete,释放申请的内存空间。下面代码删除以上申请的内存空间。,delete p,;,delete p1,;,delete p2,;,使用,new,和,delete,需要注意一下几点:,用,new,申请的内存空间,必须用,delete,释放,对于已分配内存空间的指针,只能用,delete,释放一次;否则,系统会出现错误。,delete,释放的内存空间,必须是,new,分配内存空间的首地址,要访问,new,所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问,.,16,2025/4/19 周六,1.7,运算符的优先级,优先级指定了表达式求值的运算顺序,运算顺序一般符合数学中的数学运算规则。为保持这种运算顺序,需要为运算符设置优先级,优先级高的运算符先运算,优先级低的运算符后运算,优先级相同的运算符由运算符的结合性决定运算顺序。如果有必要,可以使用括号等运算符改变表达式的运算顺序。,下面列出了从高到底运算符的优先级。同一行中的运算符具有相同的优先级,此时这些运算符的结合方向决定了求值顺序,17,2025/4/19 周六,结合方向,运算符,自左至右,-,自左至右,!,+,-(,负号,),(),*,(指针),&,(地址运算符),sizeof,自左至右,*,(乘法),/%,自左至右,+,、,-,自左至右,自左至右,、,、,=,自左至右,=,、!,=,自左至右,&,自左至右,自左至右,|,自左至右,&,自左至右,|,自左至右,?,:,自右至左,=+=-=*=/=%=&=|=,自左至右,,,18,2025/4/19 周六,表达式,表达式,(expression),是由运算符、括号和操作数,(operand),构成的序列,在运行时能计算出一个值的结果。,按运算符的不同,可将表达式分为算术表达式、赋值表达式、关系表达式、逻辑表达式、逗号表达式等等。按表达式能否放在赋值号的左边还是右边,可将表达式分为左值表达式和右值表达式,表达式按照其中运算符的优先级和结合性来求值。每个表达式都有确定的运算结果,(,表达式的值,),和所属类型,(,即结果值的类型,),,其类型取决于表达式中的运算符和操作数的类型。,19,2025/4/19 周六,类型转换,每个运算符的操作数的个数及类型都有特定限制。在一个表达式中,运算符的某个操作数如果不符合类型要求,就要对操作数进行类型转换。,C+,中的类型转换有,3,种:自动类型转换、赋值转换和强制类型转换。,3.1,自动类型转换,自动类型转换,(,也称隐式类型转换,),指系统自动地将表达式中的操作数转换成所需类型的值,转换顺序为由范围较小的类型向范围较大的类型转换。,char,bool short int float double,unsigned char unsigned short unsigned int float double,例如定义两个变量,a,和,f,:,int a=100;float f=32.2f;,则计算以下表达式:,a/f,处理过程为:先将,a,的值转换成,float,型,然后再进行浮点数的除法运算,结果为一个,float,值,3.10559,。在这个过程中,a,变量的值不改变。,规则,1,两个不同类型的操作数进行运算时,先将较小范围的数值转换为另一个较大范围的数值,然后再进行计算。各种基本数据类型的数值范围从小到大排列次序如下:,20,2025/4/19 周六,按规则,1,可知,两个有符号的值之间进行算术运算,其结果是有符号的。一个无符号的值与一个浮点数,(,如,float),进行算术运算,其结果是浮点数,(,如,float),。但两个无符号的值之间进行算术运算,(,两个值中没有,unsigned int),,其结果是有符号的,int,。,例如:,unsigned char c1=2;unsigned short s1=3;,cout(c1*s1)endl;/,乘法,输出,6,couttypeid(c1*s1).name()endl;/,输出,int,规则,2,对于,bool,、,char,、,short,、,int,类型,任意两个值之间进行算术运算、位运算,其结果都是一个,int,值。任意两个值之间进行逻辑运算,其结果都是一个,bool,值。注意规则,2,是规则,1,的一个例外。,char c1=-2;,short s1=3;,cout(c1*s1)endl;/,乘法,输出,-6,couttypeid(c1*s1).name()endl;/,输出,int,cout(c1/,按位与,输出,2,couttypeid(c1/,输出,int,cout(c1 /,逻辑与,输出,1,,表示,true,couttypeid(c1/,输出,bool,21,2025/4/19 周六,规则,3,。对于,bool,、,char,、,short,、,int,类型,任一个类型值,(,无论是否带符号,),与,unsigned int,之间进行算术运算,其结果都是,unsigned int,类型。注意规则,3,又是规则,2,的一个例外。,例如:,char c1=-2;,unsigned short s1=2;,unsigned int j=3;,cout(c1*j)endl;/,乘法,输出,4294967290,,而不是,-6,couttypeid(c1*j).name()endl;/,输出,unsigned int,cout(s1*j)endl;/,乘法,输出,6,couttypeid(s1*j).name()endl;/,输出,unsigned int,由上面例子可知,,unsigned int,被系统认为是整型数值中的最大范围的类型,因此其它类型与之计算时,都要转换到,unsigned int,。此时如果另一个整数恰好是负值,而结果是不带符号的正值,就不能得到预期结果,但二进制结果是正确的,例如,4294967290,与,-6,是一样的。,在处理表达式的过程中,并不是将变量直接转换成最大范围的类型,而是在表达式处理过程中,按照需要逐步进行转换,例如,:,int i=1;char ch=2;,float f=3.0f;double df=4.0;,cout(ch*i+f*2.0-df),较小范围的整数,(,如,short),,将截断高位字节,仅保留低位字节的值。如果原先数值大于小范围类型可表示的范围,将将出错。,较小范围的整数,(,如,char,或,float),较大范围的整数,(,如,int,或,double),,将保持原值不改变。,赋值类型转换出现在初始化表达式或者赋值表达式中。当初始化或赋值运算符的左值表达式的类型与右值表达式类型不同,且类型兼容时,将进行类型转换到左值类型。即先计算出右值表达式的值,然后将其转换为左值类型后再赋给左值。,23,2025/4/19 周六,一般情况下,编译器对于可能导致数据丢失的情形会给出警告,但不完全。不经意之间就可能产生意料不到的结果。例如:,int i=2,j=4;,double df;,df=i/j*100;/i/j,的值为,0,,而不是,0.5,cout df=df t;/,输出,0,,而不是,50,i=4.6,j=5.7;/,编译时给出警告,float x=i+j;/x,的值并不是,10.3,,而是,9=4+5,cout x=x n;/,输出,9,3.3,强制类型转换,强制类型转换,(,也称显式类型转换,),是由程序员用类型转换运算符明确指明的一种转换操作,将一个表达式强制转换到某个指定类型。强制类型转换的一般形式为:,(),或者,(),24,2025/4/19 周六,例如:,int a=7,b=2;,double y1=a/b,此时,y1,的值是,3.0,。但如果程序员希望得到,3.5,,就要对除法的操作数进行强制类型转换如下:,y1=double(a)/b,或者,y1=(double)a/b,或者,y1=a/double(b);,关于强制类型转换,说明以下两点。,(1),一个强制类型转换是否正确取决于所处理的值的范围,一般来说,强制转换是不安全的。,(2),类型强制转换作用于一个表达式,并非作用于数据存储单元,即不改变变量存储的类型和值,例如:,double width=2.36,height=5.5,area1;,int area2=int(width)*int(height);,/area1,值为,10,width,和,height,仍为,double,型,area1=width*height;/area2,值为,12.98,何时需要进行强制类型转换?,只有当自动类型转换和赋值转换不能达到目的时,才使用强制类型转换。,一般来说,强制类型转换是不安全的,只有在特定条件下执行才比较安全,所以应谨慎使用。,25,2025/4/19 周六,
展开阅读全文