|
4.7 运算符 |
运算符是用于对数据进行各种运算操作的符号。C51语言中有算术运算符、关系运算符、逻辑运算符、位运算符这几类运算符。还有些用于辅助完成复杂功能的特殊运算符。这些运算符的使用规则与标准的C语言或者其他高级语言极为类似。
算术运算符是实现数据之间算术运算的运算符。C51语言中的算术运算符可以细分为基本算术运算符和自增自减算术运算符。
1.基本算术运算符
基本算术运算包括加(+)、减(-)、乘(*)、除(/)及取模(%)运算。
●“-”运算符:进行减法或取负的运算。用于减法时为双目运算符,例如6−1=5。用于取负时为单目运算符,例如-6。
●“+”运算符:进行加法运算,双目运算符。例如6+1=7。
●“*”运算符:进行乘法运算,双目运算符。例如2*3=6。
●“/”运算符:进行除法运算,双目运算符,运算结果为除法结果的整数部分。例如7/2=3。
●“%”运算符:进行模运算,为双目运算符。运算结果为除法结果的余数部分。例如7%2=1。另外该运算符不能应用于浮点型数据的操作。
【范例4-9】 示例代码4-9是一个基本算术运算符的示例程序。
示例代码4-9
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
x1+x2=22
x1-x2=12
x1*x2=85
x1/x2=3
x1%x2=2
-x1=-17
【代码解析】在该程序中,首先定义了整型变量,然后进行初始化,接着分别采用基本算术运算符进行运算并输出结果。
2.自增自减运算符
自增自减运算符提供了简单方便的运算,其继承于标准的C语言,其功能如下:
●自增运算符“++”:进行自增(增1)运算,表示操作数加1,即x++等同于x=x+1。
●自减运算符“−−”:进行自减(减1)运算,表示操作数减1,即x−−等同于x=x−1。
注意: 这里的操作数必须为变量,不能为常量。例如,5++在编译器中是不被认可的。
除此之外,自增和自减运算符也可以放在操作数的前面。例如,++x同样表示x=x+1,−−x同样表示x=x−1。这两者的不同之处在于表达式中的用法。当自增运算符在操作数之前时,C51编译器在引用操作数之前就先执行加1操作,例如z=++x,表示x先自增1然后赋值给变量z;当自增运算符在操作数之后时,C51编译器就先引用操作数的值,而后再进行加1操作,例如z=x++,表示x先赋值给z然后自增1。自减运算符与之类似。
【范例4-10】 示例代码4-10是一个自增自减运算符的示例程序。
示例代码4-10
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
z1=-5,z2=16
z1=-7,z2=29
【代码解析】在该程序中,首先定义了整型变量x、y、z1和z2,然后对x和y进行初始化,接着进行自增自减的计算。x首先赋值给z1,然后再自增1变为−4,因此z1为−5。y首先自减1变为16,然后赋值给z2,因此z2为16。接下来,读者可以按照从左至右的运算顺序来分析下面两个表达式的值。
一般来说,在C51程序中应尽量使用自增自减运算符。这不仅是因为自增自减运算符书写更加简练。另一方面,从编译的角度来看,采用自增和自减操作符所生成的程序代码,比等价的赋值语句生成的代码执行得要快。
逻辑运算符用于表示操作数之间的逻辑运算。这里的操作数可以是整型数据、浮点型数据,以及字符型数据。而且,操作数可以是变量,也可以是常量。C51中规定,非零的操作数均按照逻辑真来对待,为零的操作数按照逻辑假来对待。C51中的逻辑运算符主要有如下几种,逻辑真值表如表4-4所示。
表4-4 逻辑运算符的真值表
●逻辑非运算符!,单目运算符,只需一个操作数,例如!5=0。
●逻辑与运算符&&,双目运算符,需要两个操作数,例如1&&2=1。
●逻辑或运算符||,双目运算符,需要两个操作数,例如17||0=1。
【范例4-11】 示例代码4-11是一个逻辑运算符的示例程序。
示例代码4-11
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a=0,b=10,c=0,d=1,e=1
17&&0=0,17||0=1
【代码解析】在该程序中,首先定义了整型变量a~e,接着对a和b进行赋值。然后通过a和b之间的逻辑运算为c、d和e赋值。最后,使用printf语句输出a~e的结果。本例中还演示了常量之间的逻辑运算和结果输出。
关系运算符用于对操作数进行比较,比较的结果为逻辑真TRUE“1”或假FALSE“0”。关系运算符和逻辑运算符在程序运算中常常在一起联合使用。在C51语言中,常用的关系运算符有如下几种。
●“>”运算符:用于判断左边的操作数是否大于右边的操作数。
●“>=”运算符:用于判断左边的操作数是否大于等于右边的操作数。
●“<”运算符:用于判断左边的操作数是否小于右边的操作数。
●“<=”运算符:用于判断左边的操作数是否小于等于右边的操作数。
●“==”运算符:用于判断左边的操作数是否等于右边的操作数。
●“! =”运算符:用于判断左边的操作数是否不等于右边的操作数。
【范例4-12】 示例代码4-12是一个关系运算符的示例程序。
示例代码4-12
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a=0,b=0,c=1,d=0,e=0,f=1
【代码解析】在该程序中,首先定义了整型变量a~f,接着分别采用上面介绍的6种关系运算符执行比较运算,并将结果赋值给a~f。最后,使用printf语句输出a~f的结果。
位运算符是操作数中的二进制位(bit)进行逐位逻辑处理或移位的运算符。C51语言中支持位运算符,这是C51语言不同于标准C语言之处,这也展示了其兼具的低级汇编语言的能力。在C51语言中,常用的位运算符有如下几种。
●“&”运算符:用于对操作数逐位进行逻辑与(AND)运算。
●“|”运算符:用于对操作数逐位进行逻辑或(OR)运算。
●“^”运算符:用于对操作数逐位进行逻辑异或(XOR)运算。
●“~”运算符:用于对操作数逐位进行按位取补(NOT)运算。
●“>>”运算符:用于对操作数进行右移运算。
●“<<”运算符:用于对操作数进行左移运算。
在C51语言中,位运算符用于字节或者字数据,对应char和int数据类型。位操作不能用于float、double、long double、void或其他复杂数据类型。由于位运算符的特殊性,下面将对其逐个进行讲解。
说明: 除了“~”运算符外,C51语言中的位运算符均为双目运算符。
1.“&”运算符
“&”运算符即按位与运算符,用于对操作数逐位进行逻辑与(AND)运算。“&”运算符在执行时,首先将两个操作数按照二进制展开,然后将对应位进行逻辑与运算。如果两个操作数对应的二进制位均为1,则逻辑与的结果为1;否则逻辑与的结果为0。
【范例4-13】 示例代码4-13是一个“&”运算符的示例程序。
示例代码4-13
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a&b=17
【代码解析】在该程序中,首先定义了整型变量a~c,接着为a赋值23,为b赋值217。然后a和b进行按位与运算。将23展开为二进制数是00010111,将217展开为二进制数是11011001。变量a和b按位与运算的结果为a&b=17(00010001)。
注意: 位运算符“&”和逻辑与运算符“&&”是不同的,很容易混淆。逻辑与运算符“&&”的结果为0或1,而位运算符“&”的运算结果通过逐位计算得到,其结果可为任意整数值。
2.“|”运算符
“|”运算符即按位或运算符,用于对操作数逐位进行逻辑或(OR)运算。“|”运算符在执行时,首先将两个操作数按照二进制展开,然后将对应位进行逻辑或运算。如果两个操作数对应的二进制位均为0,则逻辑或的结果为0;否则逻辑或的结果为1。
【范例4-14】 示例代码4-14是一个“|”运算符的示例程序。
示例代码4-14
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a|b=223
【代码解析】在该程序中,首先定义了整型变量a~c,接着为a赋值23,为b赋值217。然后a和b进行按位或运算。将23展开为二进制数是00010111,将217展开为二进制数是11011001。变量a和b按位或运算的结果为a|b=223(11011111)。
注意: 位运算符“|”和逻辑或运算符“||”是不同的,很容易混淆。逻辑或运算符“||”的结果为0或1,而位运算符“|”的运算结果通过逐位计算得到,其结果可为任意整数值。
3.“^”运算符
“^”运算符即按位异或运算符,用于对操作数逐位进行逻辑异或(XOR)运算。“^”运算符在执行时,首先将两个操作数按照二进制展开,然后将对应位进行逻辑异或运算。如果两个操作数对应的二进制位不同,则逻辑异或的结果为1;否则逻辑异或的结果为0。
【范例4-15】 示例代码4-15是一个“^”运算符的示例程序。
示例代码4-15
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a^b=206
【代码解析】在该程序中,首先定义了整型变量a~c,接着为a赋值23,为b赋值217。然后a和b进行按位异或运算。将23展开为二进制数是00010111,将217展开为二进制数是11011001。变量a和b按位异或运算的结果为a|b=206(11001110)。
4.“~”运算符
“~”运算符即按位取补运算符,用于对操作数逐位进行逻辑取反(NOT)运算。“~”运算符在执行时,首先将操作数按照二进制展开,然后逐位进行逻辑取反运算。如果操作数对应的二进制位为1,则逻辑取反的结果为0;否则逻辑取反的结果为1。
【范例4-16】 示例代码4-16是一个“~”运算符的示例程序。
示例代码4-16
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
~a=-24
【代码解析】在该程序中,首先定义了整型变量a和c,接着为a赋值23,将23展开为二进制数是00010111。变量a按位取补后的结果为~a=−24(11101000)。
5.“>>”运算符
“>>”运算符即逻辑右移运算符,用于对操作数进行右移运算,其形式为“变量名(或操作数)>>右移位数”。“>>”运算符在执行时,首先将操作数按照二进制展开,然后逐位右移指定的位数。另外,对无符号位操作数左端补0,如果为负数,即符号位为1,则左端补1。
【范例4-17】 示例代码4-17是一个“>>”运算符的示例程序。
示例代码4-17
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a>>2=54
【代码解析】在该程序中,首先定义了整型变量a和c,接着为a赋值217,将217展开为二进制数是11011001。变量a右移两位后的结果为a>>2=54(00110110)。
6.“<<”运算符
“<<”运算符即逻辑左移运算符,用于对操作数进行左移运算,其形式为“变量名(或操作数)<<左移位数”。“<<”运算符在执行时,首先将操作数按照二进制展开,然后逐位左移指定的位数。当某位从一端移出时,另一端移入0。
【范例4-18】 示例代码4-18是一个“<<”运算符的示例程序。
示例代码4-18
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a<<3=184
【代码解析】在该程序中,首先定义了整型变量a和c,接着为a赋值23,将23展开为二进制数是00010111。变量a左移三位后的结果为a<<3=184(10111000)。
除了前面介绍的几种最常见的运算符外,C51中还提供了“,”运算符、“?”运算符、地址操作运算符、联合操作运算符、sizeof运算符和类型转换运算符。这些运算符和其他高级语言类似,下面将逐个进行分析。
1.“,”运算符
“,”运算符提供了对多个连续运算的一种简便书写方式。在使用时,“,”运算符将多个表达式串接在一起,并用括号括起来。在计算时,按照从左至右的顺序计算各个表达式,并将最后一个表达式的值作为整个表达式的值返回。
【范例4-19】 示例代码4-19是一个“,”运算符的示例程序。
示例代码4-19
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
c=68
【代码解析】在该程序中,首先定义了整型变量a、b和c,接着为a赋值11,为b赋值27。然后,使用“,”运算符为变量c赋值。这里首先计算++a,a变为12,然后执行b++,b变为28,接着计算a+b,但不影响a和b的值,最后计算2*b+a,结果为68并赋值给c。
2.“?”运算符
“?”运算符提供了对if−then−else语句的一种简便书写方式,它是一种三目运算符。“?”运算符一般由“?”和“:”将表达式连接起来,其一般形式如下:
EXP1?EXE2:EXP3;
“?”运算符在执行的时候,首先计算表达式EXP1值,如果其值为True,则计算表达式EXP2的值,并将其结果作为整个表达式的结果;如果表达式EXP1的值为False,则计算表达式EXP3的值,并将其作为整个表达式的结果。
【范例4-20】 示例代码4-20是一个“?”运算符的示例程序。
示例代码4-20
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
y=200
【代码解析】在该程序中,首先定义了整型变量x和y,接着为x赋值18,然后使用“?”运算符来给变量y赋值。这里首先计算x/2是否大于9,由于x/2=9,因此该表达式为FALSE,接着将200赋值给变量y。
3.地址操作运算符
地址操作运算符用于指针类型数据的操作,地址操作运算符主要包括如下两个。
●“&”运算符:单目运算符,用于获取变量的地址;
●“*”运算符:单目运算符,用于获取该地址保存的数据值。
【范例4-21】 示例代码4-21是一个地址操作运算符的示例程序。
示例代码4-21
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a=12,b=12
【代码解析】在该程序中,首先定义了整型变量a和b,以及指针型变量p。接着为a赋值12,然后将变量a的地址赋给p,并将p地址中的内容赋值给变量b。最后,使用printf语句输出a和b的值。可以看出,在指针类型数据的运算中,“&”运算符和“*”运算符经常需要联合使用。
4.联合操作运算符
联合操作运算符为一些特殊赋值语句提供了简化的书写方式。这些特殊赋值语句一般具有如下形式:
<变量>=<变量><操作符><表达式>
如果使用联合操作运算符,则这种赋值语句可以简化为如下的形式:
<变量><操作符>=<表达式>
【范例4-22】 示例代码4-22是一个联合操作运算符的示例程序。
示例代码4-22
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
a=7
a=3
a=12
a=6
【代码解析】在该程序中,首先定义了整型变量a和b,然后分别为其赋值。接着采用联合操作运算符进行计算,联合操作运算符的含义在代码注释中均有解释。
5.sizeof运算符
sizeof运算符用于获取变量或者数据类型的长度。其实,在使用形式上sizeof运算符更类似于一个函数。
【范例4-23】 示例代码4-23是一个sizeof运算符的示例程序。
示例代码4-23
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
i=12,j=4,k=2
【代码解析】在该程序中,首先定义并初始化了字符串a和整型变量b。接着使用sizeof运算符获取字符串、double数据类型和整型变量b的长度。最后,通过printf语句输出结果。
6.类型转换运算符
类型转换运算符用于对操作数或者表达式的数据类型进行强制转换,其一般形式为“(类型)变量”或者“(类型)表达式”。
【范例4-24】 示例代码4-24是一个类型转换运算符的示例程序。
示例代码4-24
【运行结果】使用Keil μVision3编译程序可以完成该程序的编译和仿真操作。该程序运行时输出如下结果:
i=80,ch=P,d=80.000000
【代码解析】在该程序中,首先定义变量,接着为变量i赋值,然后通过强制类型转换为字符型变量ch和浮点型变量d赋值。最后,通过printf语句输出结果。
在一个程序中,运算符一般不会单独使用,经常是多个运算符联合进行运算。此时,便有运算符优先级的问题,即先计算哪个的问题。在C51语言中,所有运算符的优先级如表4-5所示。其中,最常用的运算符优先级规则如下:
●算术运算符的优先级由高到低依次为自增自减(++、−−)和取负(−)、乘法除法(*、/)和取模(%)、加和减(+、−)。
●括号的优先级最高,所以括号会改变计算顺序。
●关系运算符和逻辑运算符的相对优先级最高的是!,其次是>、<、>=和<=,然后是==和! =,再后面是&&,最后是||。
●关系和逻辑运算符的优先级比算术运算符低,即像表达式10>1+12和表达式10>(1+12)计算的结果是一样的。
●在关系或逻辑表达式中可以使用括号修改原计算的优先级顺序。
表4-5 运算符的优先级
结合性是在执行运算时需要注意的另一个问题。运算符的结合性分为左结合性和右结合性两种。左结合性是指变量(或常量)与左边的运算符结合,右结合性是指变量(或常量)与右边的运算符结合。
在C51语言中,一般来说,对于双目运算符均为左结合性,而对于单目运算符和双目运算符则均为右结合性。因此,读者可以通过表4-5很容易地判断运算符的结合性。