第4章 运算符
(视频讲解:1小时39分钟)
表达式在C#程序中应用广泛,尤其是在计算功能中,往往需要大量的表达式。而大多数表达式都使用运算符,运算符结合一个或一个以上的操作数,便形成了表达式,并且返回运算结果。本章将对C#中的表达式与运算符进行详细讲解。
通过学习本章,读者主要掌握以下内容:
熟悉算术运算符的使用
掌握赋值运算符
掌握如何使用关系运算符
掌握逻辑运算符的使用方法
掌握如何对变量进行位操作
熟悉运算符的优先级顺序
4.1 运算符分类
视频讲解
运算符是具有运算功能的符号,根据使用运算符的个数,可以将运算符分为单目运算符、双目运算符和三目运算符,其中,单目运算符是作用在一个操作数上的运算符,如正号(+)等;双目运算符是作用在两个操作数上的运算符,如加法(+)、乘法(*)等;三目运算符是作用在3个操作数上的运算符,C#中唯一的三目运算符就是条件运算符(?:)。本节将详细讲解C#中的运算符。
4.1.1 算术运算符
C#中的算术运算符是双目运算符,主要包括+、–、*、/和% 5种,它们分别用于进行加、减、乘、除和模(求余数)运算。C#中算术运算符的功能及使用方式如表4.1所示。
表4.1 算术运算符
图4.1 3门课的成绩
【例4.01】某学员3门课成绩如图4.1所示,编程实现:
- C#课和SQL课的分数之差。
- 3门课的平均分。
代码如下:(实例位置:资源包\源码\04\4.01)
程序运行结果如图4.2所示。
图4.2 计算学生成绩的分差及平均分
注意
使用除法(/)运算符和求余运算符时,除数不能为0,否则将会出现异常。
4.1.2 自增、自减运算符
使用算术运算符时,如果需要对数值型变量的值进行加1或者减1操作,可以使用下面的代码:
针对以上功能,C#中还提供了另外的实现方式:自增、自减运算符,它们分别用++和– –表示,下面分别对它们进行讲解。
自增、自减运算符是单目运算符,在使用时有两种形式,分别是++expr、expr++,或者--expr、expr--,其中,++expr、--expr是前置形式,它表示expr自身先加1或者减1,其运算结果是自身修改后的值,再参与其他运算;而expr++、expr--是后置形式,它也表示自身加1或者减1,但其运算结果是自身未修改的值,也就是说,expr++、expr--是先参加完其他运算,然后在进行自身加1或者减1操作,自增、自减运算符放在不同位置时的运算示意图如图4.3所示。
图4.3 自增、自减运算符放在不同位置时的运算示意图
例如,下面代码演示自增运算符放在变量的不同位置时的运算结果:
注意
自增、自减运算符只能作用于变量,因此,下面的形式是不合法的:
多学两招
如果程序中不需要使用操作数原来的值,只是需要其自身进行加(减)1,那么建议使用前置自加(减),因为后置自加(减)必须先保存原来的值,而前置自加(减)不需要保存原来的值。
4.1.3 赋值运算符
赋值运算符主要用来为变量等赋值,它是双目运算符。C#中的赋值运算符分为简单赋值运算符和复合赋值运算符,下面分别进行讲解。
1. 简单赋值运算符
简单赋值运算符以符号“=”表示,其功能是将右操作数所含的值赋给左操作数。例如:
2. 复合赋值运算符
在程序中对某个对象进行某种操作后,如果要再将操作结果重新赋值给该对象,则可以通过下面的代码实现:
上面的代码看起来很烦琐,在C#中,上面的代码等价于:
上面代码中的+=就是一种复合赋值运算符,复合赋值运算符又称为带运算的赋值运算符,它其实是将赋值运算符与其他运算符合并成一个运算符来使用,从而同时实现两种运算符的效果。
C#提供了很多复合赋值运算符,其说明及运算规则如表4.2所示。
表4.2 复合赋值运算符的说明及运算规则
3. 复合赋值运算符的优势及劣势
使用复合赋值运算符时,虽然“a += 1”与“a = a + 1”两者的计算结果是相同的,但是在不同的场景下,两种使用方法都有各自的优势和劣势,下面分别介绍。
(1)低精度类型自增
在C#中,整数的默认类型是int型,所以下面的代码会报错:
上面的代码中,在没有进行强制类型转换的条件下,a+1的结果是一个int值,无法直接赋给一个byte变量。但是如果使用“+=”实现递增计算,就不会出现这个问题,代码如下:
(2)不规则的多值运算
复合赋值运算符虽然简洁、强大,但是有些时候是不推荐使用的,例如下面的代码:
上面的代码如果改成复合赋值运算符实现,就会显得非常烦琐,代码如下:
说明
在C#中可以把赋值运算符连在一起使用。例如:
在这个语句中,变量x,y,z都得到同样的值5,但在程序开发中不建议使用这种赋值语法。
4.1.4 关系运算符
关系运算符是双目运算符,它用于在程序中的变量之间,以及其他类型的对象之间的比较,它返回一个代表运算结果的布尔值。当运算符对应的关系成立时,运算结果为true,否则为false。关系运算符通常用在条件语句中来作为判断的依据。C#中的关系运算符共有6个,其使用及说明如表4.3所示。
表4.3 关系运算符
说明
不等运算符(!=)是与相等运算符相反的运算符,它与!(a==b)是等效的。
【例4.02】创建一个控制台应用程序,声明3个int类型的变量,并分别对它们进行初始化,然后分别使用C#中的各种关系运算符对它们的大小关系进行比较,代码如下:(实例位置:资源包\源码\04\4.02)
程序运行结果如图4.4所示。
图4.4 使用关系运算符比较大小关系
4.1.5 逻辑运算符
假定某面包店,在每周二的下午7点至8点和每周六的下午5点至6点,对生日蛋糕商品进行折扣让利活动,那么想参加折扣活动的顾客,就要在时间上满足这样的条件(周二并且7:00PM~8:00PM或者周六并且5:00PM~6:00PM),这里就用到了逻辑关系,C#中也提供了这样的逻辑运算符来进行逻辑运算。
逻辑运算符是对真和假这两种布尔值进行运算,运算后的结果仍是一个布尔值,C#中的逻辑运算符主要包括&(&&)(逻辑与)、|(||)(逻辑或)、!(逻辑非)。在逻辑运算符中,除了“!”是单目运算符之外,其他都是双目运算符。表4.4列出了逻辑运算符的用法和说明。
表4.4 逻辑运算符
使用逻辑运算符进行逻辑运算时,其运算结果如表4.5所示。
表4.5 使用逻辑运算符进行逻辑运算
技巧
逻辑运算符“&&”与“&”都表示“逻辑与”,那么它们之间的区别在哪里呢?从表4.5可以看出,当两个表达式都为true时,逻辑与的结果才会是true。使用“&”会判断两个表达式;而“&&”则是针对bool类型的数据进行判断,当第一个表达式为false时,则不去判断第二个表达式,直接输出结果从而节省计算机判断的次数。通常将这种在逻辑表达式中从左端的表达式可推断出整个表达式的值称为“短路”,而那些始终执行逻辑运算符两边的表达式称为“非短路”。“&&”属于“短路”运算符,而“&”则属于“非短路”运算符。“||”与“|”的区别跟“&&”与“&”的区别类似。
【例4.03】创建一个控制台应用程序,使用代码实现4.1.5节开始描述的场景—参加面包店的打折活动,代码如下:(实例位置:资源包\源码\04\4.03)
程序运行结果如图4.5和图4.6所示。
图4.5 符合条件的运行效果
图4.6 不符合条件的运行效果
4.1.6 位运算符
位运算符的操作数类型是整型,可以是有符号的也可以是无符号的。C#中的位运算符有位与、位或、位异或和取反运算符,其中位与、位或、位异或为双目运算符,取反运算符为单目运算符。位运算是完全针对位方面的操作,因此,它在实际使用时,需要先将要执行运算的数据转换为二进制,然后才能进行执行运算。
说明
整型数据在内存中以二进制的形式表示,如整型变量7的32位二进制表示是00000000 00000000 00000000 00000111,其中,左边最高位是符号位,最高位是0表示正数,若为1则表示负数。负数采用补码表示,如–8的32位二进制表示为11111111 11111111 11111111 11111000。
1. “位与”运算
“位与”运算的运算符为“&”,“位与”运算的运算法则是:如果两个整型数据a、b对应位都是1,则结果位才是1,否则为0。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图4.7所示。
2. “位或”运算
“位或”运算的运算符为“|”,“位或”运算的运算法则是:如果两个操作数对应位都是0,则结果位才是0,否则为1。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图4.8所示。
图4.7 12&8的运算过程
图4.8 4|8的运算过程
3. “位异或”运算
“位异或”运算的运算符是“^”,“位异或”运算的运算法则是:当两个操作数的二进制表示相同(同时为0或同时为1)时,结果为0,否则为1。若两个操作数的精度不同,则结果数的精度与精度高的操作数相同,如图4.9所示。
4. “取反”运算
“取反”运算也称“按位非”运算,运算符为“~”。“取反”运算就是将操作数对应二进制中的1修改为0,0修改为1,如图4.10所示。
图4.9 31^22的运算过程
图4.10 ~123的运算过程
4.1.7 移位运算符
C#中的移位运算符有两个,分别是左移位<<和右移位>>,这两个运算符都是双目运算符,它们主要用来对整数类型数据进行移位操作。移位运算符的右操作数不可以是负数,并且要小于左操作数的位数。下面分别对左移位<<和右移位>>进行讲解。
1. 左移位运算符<<
左移位运算符<<是将一个二进制操作数向左移动指定的位数,左边(高位端)溢出的位被丢弃,右边(低位端)的空位用0补充。左移位运算相当于乘以2的n次幂。
例如,int类型数据48对应的二进制数为00110000,将其左移1位,根据左移位运算符的运算规则可以得出(00110000<<1)=01100000,所以转换为十进制数就是96(48*2);将其左移2位,根据左移位运算符的运算规则可以得出(00110000<<2)=11000000,所以转换为十进制数就是192(48*22),其执行过程如图4.11所示。
图4.11 左移位运算
2. 右移位运算符>>
右移位运算符>>是将一个二进制操作数向右移动指定的位数,右边(低位端)溢出的位被丢弃,而在填充左边(高位端)的空位时,如果最高位是0(正数),左侧空位填入0;如果最高位是1(负数),左侧空位填入1。右移位运算相当于除以2的n次幂。
正数48右移1位的运算过程如图4.12所示。
负数–80右移2位的运算过程如图4.13所示。
图4.12 正数的右移位运算过程
图4.13 负数的右移位运算过程
多学两招
由于移位运算的速度很快,在程序中遇到表达式乘以或除以2的n次幂的情况时,一般采用移位运算来代替。
4.1.8 条件运算符
条件运算符用“?:”表示,它是C#中唯一的三目运算符,该运算符需要3个操作数,形式如下:
其中,表达式1是一个布尔值,可以为真或假,如果表达式1为真,返回表达式2的运算结果,如果表达式1为假,则返回表达式3的运算结果。例如:
技巧
条件运算符相当于一个if语句,因此,上面的第2行代码可以修改如下:
关于if语句的详细讲解,请参见第5章。
另外,条件运算符的结合性是从右向左的,即从右向左运算,例如:
等价于:
【例4.04】创建一个控制台应用程序,使用条件运算符判断输入年龄所处的阶段,并输出相应的提示信息,代码如下:(实例位置:资源包\源码\04\4.04)
程序运行结果如图4.14所示。
图4.14 使用条件运算符判断人的年龄阶段
4.2 运算符优先级与结合性
视频讲解
C#中的表达式是使用运算符连接起来的符合C#规范的式子,运算符的优先级决定了表达式中运算执行的先后顺序。运算符优先级其实相当于进销存的业务流程,如进货、入库、销售、出库,只能按这个步骤进行操作。运算符的优先级也是这样的,它是按照一定的先后顺序进行计算的,C#中的运算符优先级由高到低的顺序依次是:
(1)单目运算符。
(2)算术运算符。
(3)移位运算符。
(4)关系运算符。
(5)逻辑运算符。
(6)条件运算符。
(7)赋值运算符。
如果两个运算符具有相同的优先级,则会根据其结合性确定是从左至右运算,还是从右至左运算。表4.6列出了运算符从高到低的优先级顺序及结合性。
表4.6 运算符的优先级顺序及结合性
说明
表4.6中的“←”表示从右至左,“→”表示从左至右,从表4.6中可以看出,C#中的运算符中,只有单目、条件和赋值运算符的结合性为从右至左,其他运算符的结合性都是从左至右,所以,下面的代码是等效的:
4.3 小结
本章主要的内容是运算符,几种常用的运算符需要读者重点掌握。在应用程序开发中,运算符被频繁地使用,可见其重要性。最常见的运算符有算术运算符、自增自减运算符、赋值运算符、关系运算符、逻辑运算符、位运算符以及其他一些特殊的运算符。如果表达式中需要同时存在几个运算符,那么就必须考虑运算符的优先级顺序,优先级顺序高的要比优先级顺序低的先被执行。
4.4 实战
4.4.1 实战一:谁家是“超生游击队”
国家推出二胎政策,A家庭陆续生了2个孩子,B家庭陆续生了4个孩子,哪个家庭属于超生家庭?实战效果如图4.15所示。(实例位置:资源包\源码\04\实战\01)
图4.15 谁家是“超生游击队”
4.4.2 实战二:模拟用户登录
使用逻辑运算符判断用户输入的用户名和密码是否同时满足条件,如果是,输出“登录成功”,否则,输出“登录失败”。提示:默认的用户名和密码分别是mr和mrsoft,另外,该程序实现时需要用到关系运算符“==”和条件运算符“?:”。登录成功和失败的效果分别如图4.16和图4.17所示。(实例位置:资源包\源码\04\实战\02)
图4.16 登录成功
图4.17 登录失败