C51单片机高效入门(第2版)
上QQ阅读APP看书,第一时间看更新

3.2 数据类型、运算符与表达式

3.2.1 C语言的数据类型

计算机中的程序,离不开数据个这个单元,它是计算机操作的对象,我们将数据的不同格式叫做数据类型;而数据结构则是按一定的数据类型进行一些组合和构架。

在C语言中,数据类型可分为:基本数据类型,构造数据类型,指针类型,空类型四大类,如图3-1所示。

978-7-111-30335-0-Chapter03-3.jpg

图3-1 数据类型

下面我们来看一下这4种数据类型的特点:

(1)基本数据类型:它的数据不可以再进行分解。

(2)构造数据类型:根据已定义的一个或多个数据类型用构造的方法来定义的。也就是说,一个构造类型的值可以由若干个“成员”或“元素”组成。其“成员”可以是一个基本数据类型或构造类型。在C语言中,构造类型有以下几种:

1)数组类型

2)结构体类型

3)共用体类型

(3)指针类型:指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址,也是C语言的精华所在。虽然指针变量的值类似于整型量,但从其数据类型意义来看,这是两种完全不同的类型,所以不能混为一谈。

(4)空类型:函数在被调用完成后,通常会返回一个函数值。函数值有一定的数据类型的,应在函数定义及函数说明中加以说明,如在例3-2中给出的min函数定义中,函数头为:int min(int a,int b);其中“int”表示min函数的返回值为整型数据类型。但是,我们也经常会碰到一些函数,调用后并不需要向调用者返回函数值,这时,我们应该如何来书写呢?此时,我们将这种函数定义为“空类型”,其类型说明符为void。如将int min(int a,int b);改为void min(int a,int b);即可。

在本节中,我们先介绍基本数据类型中的整型、浮点型和字符型。其余类型在以后各节中陆续介绍。

3.2.1.1 常量与变量

对于基本数据类型量,根据变量值在程序执行过程中是否发生变化,又可分为常量和变量两种。在程序执行过程中,其值不发生改变的量称为常量,其值可变的量称为变量。每个变量都会有个名字,并在内存中占据一定的存储单元,所占存储单元的数量根据数据类型的不同而不同。在程序中,常量是可以不经说明而直接引用的,而变量则必须先定义后使用。

1.常量和符号常量

常量与变量相对应,在程序执行的过程中,其值不能发生改变的量称为常量。常量与变量不同,可以有不同的数据类型,如:1,3,5为整型常量;6.9,-1.85为实型常量;‘c’,‘d’为字符常量。常量可以用一个标识符来说明,符号常量在使用之前必须先定义,其一般形式为

#define标识符 常量

其中,#define是一条编译预处理命令(预处理命令都以“#”开头),称为宏定义命令,其功能是把该标识符定义为其后的常量值。一经定义,凡在程序中所有出现该标识符的地方均用之前定义好的常量来代替。习惯上符号常量的标识符用大写字母来表示,变量标识符用小写字母,以示区别。

如下面的程序所示:

【例3-3】

978-7-111-30335-0-Chapter03-4.jpg

程序的第一句话“#define PI 3.14”定义了一个符号常量PI,它的值为圆周率3.14,由此一来,在后面的程序代码中,凡是出现PI的地方,都代表3.14这个数。

使用符号常量的好处是:意义清楚;修改参数非常方便。如程序中很多地方都要用到这个变量,而数值又需要经常做改动,这样的情况下使用符号常量就可以做到“一改全改,一次改成”。

2.变量

在程序运行中,其值可以改变的量称为变量。一个变量有一个名字,在内存中占据一定的存储单元,如图3-2所示。变量必须先定义再使用,一般放在函数体的开头部分,当然,如果是全局变量的话,就放在函数体外面了。

3.2.1.2 整型数据

978-7-111-30335-0-Chapter03-5.jpg

图3-2 变量与存储单元

1.整型常量的表示方法

整型常量就是整型的常数。在C语言中,整型常量可以分为八进制、十进制和十六进制三种。

1)十进制数没有前缀。用数码0~9来表示。

如以下各数是合法的十进制整型常数:

257、-518、55535、1968;

在程序中是根据前缀来区分各种进制数的。因此在书写常数时不要把前缀弄错造成结果不正确。

2)八进制数:必须以0开头,即以0作为八进制数的前缀。用数码0~7来表示。八进制数通常是无符号数。

以下各数是合法的八进制数:

016(相当于十进制数14)、0110(相当于十进制数72)、0177776(相当于十进制数65534);

以下各数不是合法的八进制数:

156(无前缀0)、01B2(包含了非八进制数字)、-0170(不应该有负号)。

3)十六进制数:以0X或0x开头。其数码取值为0~9,A~F或a~f。

以下各数是合法的十六进制整常数:

0X2B(相当于十进制43)、0XB0(相当于十进制176)、0XFFFF(相当于十进制65535);

以下各数不是合法的十六进制整常数:

7B(无前缀0X)、0X5H(含有非十六进制数码)。

2.整型变量

(1)整型变量的分类

1)基本型:类型说明符为int,在内存中占2个字节。

2)无符号型:类型说明符为unsigned。无符号型又可为unsigned int或unsigned。无符号类型量所占的内存空间字节数与相应的有符号类型量相同,但由于省去了符号位,故不能表示负数,也因此表示正数的数值范围有所扩大。

有符号整型变量:最大表示32767

978-7-111-30335-0-Chapter03-6.jpg

无符号整型变量:最大表示65535

978-7-111-30335-0-Chapter03-7.jpg

KEIL uVision2 C51编译器所支持的数据类型见表3-1。

表3-1 数据类型表

978-7-111-30335-0-Chapter03-8.jpg

(2)整型变量的定义:变量定义的一般形式为

类型说明符 变量名标识符,变量名标识符,...;

例如:

int a,b,c;(a,b,c为整型变量)

unsigned int x,y;(x,y为无符号整型变量)

定义变量时,允许在一个类型说明符后,同时定义多个相同类型的变量。这时,各变量名之间用逗号间隔,类型说明符与变量名之间至少用一个空格间隔。

【例3-4】整型变量的定义与使用。

978-7-111-30335-0-Chapter03-9.jpg

3.2.1.3 实型数据

1.实型常量的表示方法

实型数据在C语言中也称为浮点型。在C语言中,它有两种表示形式:分别为十进制小数形式和指数形式。

1)十进制数形式:由数码0~9和小数点组成。

例如:

0.0、24.0、5.189、0.93、90.0、6000.、-227.8670

等均为合法的实数。在这个数中是必须存在小数点的。

2)指数形式:在十进制数的基础上,加阶码标志“e”或“E”和阶码(只能为整数,可以带正负号)组成。

其一般形式为:

a E n(a为十进制数,n为十进制整数)

表示的数值为a*10n

例如:

3.1E4(等于3.1*104

1.6E-2(等于1.6*10-2

0.8E7(等于0.8*107

-1.14E-2(等于-1.14*10-2

以下不是合法的实数:

385(无小数点)

E6(阶码标志E之前无数字)

-1(无阶码标志)

51.-E3(负号位置不对)

2.9E(无阶码)

标准C允许浮点数尾部加后缀,“f”或“F”即表示该数为浮点数。如328f和328.是等价的。

【例3-5】通过以下程序例子可以看到其显示输出值都是一样的。

978-7-111-30335-0-Chapter03-10.jpg

2.实型变量

实型变量主要分为:单精度(float型)和双精度(double型),见表3-2。

表3-2 float与double类型数据

978-7-111-30335-0-Chapter03-11.jpg

在C编译器中单精度型占4个字节(32位)内存空间,其数值范围为3.4E-38~3.4E+38,只能提供七位有效数字。双精度型占8个字节(64位)内存空间,其数值范围为1.7E-308~1.7E+308,提供16位有效数字。

关于实型变量定义的方法与整型相同,参考如下:

例如:

float x,y;(x,y为单精度实型量)

double a,b,c;(a,b,c为双精度实型量)

3.2.1.4 字符型数据

字符型数据可分为字符常量和字符变量。

1.字符常量

我们用单引号括起来的一个字符,称为字符常量。

例如:

‘y’、‘f’、‘-’、‘+’、‘/’

这些都是合法字符常量。

我们在使用字符常量时,需要注意以下几点:

1)字符常量只能用单引号括起来,如果用双引号或其他括号,将出现错误提示。

2)字符常量只能是单个字符,不能出现多个字符。

3)字符可以是字符集中任意字符。但如果将数字定义为字符型常量后,它就不能参加数值运算了。如‘8’和8,仔细看一下是不同的。前者是字符常量,不能参与运算,而后者才是一个整型数据。

2.转义字符

转义字符在C语言中是一种特殊的字符常量。转义字符是以反斜线“\”开头的字符序列。不同的转义字符具有不同的特定含义,因此我们称它为“转义”字符。例如,我们经常在例题中看到的printf函数中用到的“\n”就是一个转义字符,其意义是“回车换行”。转义字符的作用主要用来表示有些用一般字符不便于表示的语句代码。表3-3列出了C语言常用的转义字符及含义。

表3-3 C语言常用的转义字符及含义

978-7-111-30335-0-Chapter03-12.jpg

可以说,C语言字符集中的任何一个字符均可用转义字符来表示。表中的\ddd和\xhh正是起到了这个作用。ddd和hh分别为八进制ASCII码和十六进制ASCII码。如\102表示字母“B”,\103表示字母“C”,\XOA表示换行等。

【例3-6】转义字符的使用方法。

978-7-111-30335-0-Chapter03-13.jpg

大家可以想一下,这个程序的输出结果是怎样的。

3.字符变量

字符变量用来存储单个字符。

字符变量的类型说明符是char。其定义方法与上面我们所讲的数据类型定义方法一样。

例如:

char a,b;

char数据类型的长度是1个字节,通常用于定义处理字符数据的变量或常量。unsigned char与signed char数据类型的区别是有无符号位,如果我们直接使用char类型的话,其默认值为signed char类型。因为unsigned char类型1个字节所有的8位均来表示数值,而signed char类型用字节中的最高位bit来表示数据的正负,“0”表示其为正数,“1”表示为负数,所以unsigned char类型可以表达的数值范围是0~255,而signed char类型可以表达的数值范围是-128~+127。

4.字符串常量

字符串常量是由一对双引号括起的字符序列。例如:“CHINA”,“C program”,“$1.3”等都是合法的字符串常量。

那么字符串常量与字符常量之间有什么不同之处呢。总结一下,主要有以下几点:

1)字符常量使用单引号括起来,而字符串常量使用双引号括起来。

2)字符常量只能是单个字符,而字符串常量却可以是一个或多个字符。

3)可以把一个字符常量赋给一个字符变量,但反过来把一个字符串常量赋予一个字符

变量是不行的。在C语言中是没有相应的字符串变量的。

4)字符常量在内存中占1个字节的空间。字符串常量在内存中所占字节的大小等于字符串的字节数加1,这是一个字符串结束的标志位,在C语言中,用字符‘\0’作为字符串的结束标志,‘\0’的ASCII码值为0。

例如:

字符串“C program”在内存中所占字节的情况为

978-7-111-30335-0-Chapter03-14.jpg

‘\0’为字符串“C program”结束的标志位。

字符常量‘b’和字符串常量“b”,从表面上来看,虽然都只有一个字符,但在内存中的存储情况却是不同的。

‘b’在内存中占1个字节,其存储情况为:

978-7-111-30335-0-Chapter03-15.jpg

“b”在内存中占2个字节,其存储情况为:

978-7-111-30335-0-Chapter03-16.jpg

3.2.1.5 变量赋初值在程序中常常需要对变量赋于一个初值。C语言可以在做变量定义的同时,给变量赋上

初值,我们也称之为变量的初始化操作;也可以在后面的语句中再给变量赋上值。变量初始化赋初值的一般形式为

类型说明符 变量1=值1,变量2=值2,变量3=值3……;

例如:

978-7-111-30335-0-Chapter03-17.jpg

与其他高级编程语言不同,C语言在变量定义中不允许出现多个“=”赋值符号,如a=b=c=3是非法的。

【例3-7】

978-7-111-30335-0-Chapter03-18.jpg

3.2.1.6 各类数值型数据之间的混合运算

变量的数据类型是可以转换的。转换方式有两种,一种是自动类型转换,另一种是强制类型转换。当程序中不同数据类型的变量混合运算时,自动类型转换由编译器自动完成。自动类型转换遵循以下转换规则:

1)若参与运算的数据类型不一样,则先转换成同一数据类型,再进行运算。

2)转换以保证精度不降低的原则进行。

3)所有的浮点运算都是转换成双精度进行的,既使表达式中仅含float单精度的数据运算量,也要先转换成double型,再作运算处理。

4)在赋值运算中,当赋值号两边的数据类型不同时,赋值号右边的数据类型将转换成左边的数据类型。如果右边的数据类型长度大于左边的数据类型长度时,那它将丢失一部分数据,丢失的部分遵循四舍五入的原则,降低了精度。

类型自动转换的规则如图3-3所示。

当int和char同时进行运算时,则系统先将char转为int型数据;当float与double型共同存在时,则先将float转为double型数据。当int,unsigned,long,doulbe类型的数据同时存在时,则其转换高低关系为int→unsigned→long→double。如果int与long共存时,则必将int转换为long类型数据。

【例3-8】

978-7-111-30335-0-Chapter03-19.jpg

978-7-111-30335-0-Chapter03-20.jpg

图3-3 不同数据之间的自动类型转换

例3-8中,变量PI为实型;s,r为整型。当程序运行到s=r*r*PI语句时,变量r和变量PI都将转换成double型数据,其计算结果也为double类型,但因为s为整型变量,根据自动类型转换原则,其最终结果应该为整型,如出现小数数值,则舍去了小数部分,所以,程序的执行结果,其面积s=113。

强制类型转换是通过类型转换运算符来实现的。其一般形式为

(类型名)(表达式)

其功能是把表达式的运算结果值强制转换成类型名所表示的数据类型。

例如:

978-7-111-30335-0-Chapter03-21.jpg

在使用强制转换时应注意以下两点:

1)当变量为多个时,类型名和表达式都必须加上括号(单个变量可以不加括号),如:把(int)(x+y)写成(int)x+y,其意义为把x转换成int型后再与y相加;而(int)(x+y)的意义是把x和y相加,其相加结果再转换成int数据类型。

2)强制类型转换时,并没有改变原来变量的类型,而是得到一个所需类型的中间变量,如上例中的变量x和变量y,其本身的数据类型和值并没有发生变化。

【例3-9】

978-7-111-30335-0-Chapter03-22.jpg

从以上程序执行结果可以看出,float型变量f虽然在printf函数中强制类型转为int型,但它只在运算中起作用,而且是临时的,变量f其本身的类型并不改变。因此,(int)f的值为1(截去了小数部分数字),而f的值仍为1.39。

【单片机C语言设计小实例1】先来写个小程序看看unsigned char和unsigned int用于延时的不同效果,说明它们的长度是不同的,学习它们的用法。实验中用LED1的点亮表明正在用unsigned int数值延时,用LED2点亮表明正在用unsigned char数值延时。电路原理如图3-4所示。

978-7-111-30335-0-Chapter03-23.jpg

图3-4 实验电路

实验程序如下:

978-7-111-30335-0-Chapter03-24.jpg

978-7-111-30335-0-Chapter03-25.jpg

执行编译烧写,上电运行你就可以看到结果了。很明显LED1点亮的时间要比LED2点亮的时间长。请注意,当定义一个变量为特定的数据类型时,在程序使用该变量时,不应使它的值超过数据类型的值域。如本例中的变量b不能赋超出0~255的值,如for(b=0;b<255;b++)改为for(b=0;b<256;b++),编译是可以通过的,但运行时就会有问题出现,就是说b的值必须是小于256的,所以无法跳出循环执行下一句P1—1=1,从而造成死循环。同样,a的值不应该超出0~65535的范围。

【单片机C语言设计小实例2】跑马灯实验例子,将P1口的全部引脚分别驱动1个LED发光管。电路原理如图3-5所示。

978-7-111-30335-0-Chapter03-26.jpg

图3-58 路跑马灯电路

程序如下:

978-7-111-30335-0-Chapter03-27.jpg

978-7-111-30335-0-Chapter03-28.jpg

程序中的花样数据可以自己去定义,因这里我们的LED要AT89C51的引脚P1为低电平才会点亮,所以我们要向P1口的各引脚写数据O对应连接的LED才会被点亮,P1口的8个引脚刚好对应P1口特殊寄存器的8个二进位。如向P1口定数据0xFE,转成二进制就是11111110,最低位D0为0,这里引脚P1.0输出低电平,LED1被点亮。如此类推,大家不难算出自己想要做的效果了。大家编译烧写看看,效果就会出来,显示的速度您可以根据需要调整延时a的值,不要超过变量类型的值域就行了。

【单片机C语言设计小实例3】8路跑马灯的另一种写法。

程序如下:

978-7-111-30335-0-Chapter03-29.jpg

978-7-111-30335-0-Chapter03-30.jpg

3.2.2 算术运算符和算术表达式

C语言中运算符和表达式数量之多,在高级语言中是少见的。正是丰富的运算符和表达式使C语言功能十分完善。这也是C语言的主要特点之一。

C语言的运算符不仅具有不同的优先级,而且还有一个特点,就是它的结合性。在表达式中,各运算量参与运算的先后顺序不仅要遵守运算符优先级别的规定,还要受运算符结合性的制约,以便确定是自左向右进行运算还是自右向左进行运算。这种结合性是其他高级语言的运算符所没有的,因此也增加了C语言的复杂性。

3.2.2.1 C运算符简介

C语言的运算符可分为以下几类:

1)算术运算符:用于各类数值运算。包括加(+)、减(-)、乘(*)、除(/)、求余(或称模运算,%)、自增(++)、自减(--)共7种。

2)关系运算符:用于比较运算。包括大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)和不等于(!=)共6种。

3)逻辑运算符:用于逻辑运算。包括与(&&)、或(||)、非(!)共3种。

4)位操作运算符:参与运算的量,按二进制位进行运算。包括位与(&)、位或(|)、位非(~)、位异或(^)、左移(<<)、右移(>>)共6种。

5)赋值运算符:用于赋值运算。分为简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位运算赋值(&=,|=,^=,>>=,<<=)三类共11种。

6)条件运算符:这是一个三目运算符,用于条件求值(?:)。

7)逗号运算符:用于把若干表达式组合成一个表达式(,)。

8)指针运算符:用于取内容(*)和取地址(&)两种运算。

9)求字节数运算符:用于计算数据类型所占的字节数(sizeof)。

10)特殊运算符:有括号(),下标[],成员(→,.)等几种。

3.2.2.2 算术运算符和算术表达式

1.基本的算术运算符

1)加法运算符“+”:如a+b

2)减法运算符/负数值符号“-”:如a-b或负数-5

3)乘法运算符“*”:如a*b

4)除法运算符“/”:如a/b

5)求余运算符(模运算符)“%”:如11%3=2,即11除以3后,余数为2。

【例3-10】

978-7-111-30335-0-Chapter03-31.jpg

本例输出110除以3所得的余数2。

2.算术表达式和运算符的优先级和结合性

算术表达式——用算术运算符和括号将运算对象连接起来的,符合C语言语法的式子,我们称为算术表达式。其运算对象包括常量、变量、函数等。表达式求值运算按运算符的优先级和结合性来进行。

以下是算术表达式的例子:

978-7-111-30335-0-Chapter03-32.jpg

1)运算符的优先级:优先级——当一个运算对象两边都有运算符时,执行运算的先后顺序。优先级高的,则先进行运算。C语言中,运算符的运算优先级总共分为15个等级。1级最高,15级最低。在表达式中,优先级较高的比优先级较低的先进行运算。而在一个运算对象两侧的运算符优先级相同时,那就得按运算符的结合性规定进行处理。

2)运算符的结合性:结合性——当一个运算对象两边的运算符出现相等优先级情况下的运算顺序。C语言中所有运算符的结合性分为两种,左结合性(自左向右)和右结合性(自右向左)。算术运算符的结合性是自左至右,即先左后右。

例如:a+b*c,从这个表达式可以看到,乘法的优先级要高于加减法,因此,先进行b*c的运算,然后,再将其积加上a。

(a-b)*(c+d)+e,该表达式比上面的表达式稍微复杂点,因为括号在算术运算符中的优先级是最高的,故应先做括号内的运算,即完成(a-b)和(c+d)的运算,再将两个计算结果进行相乘,最后再加上e。算术运算符的结合性是自左向右的,也称“左结合性”。而自右向左的结合方向称为“右结合性”。最常见的右结合性运算符是赋值运算符。例如a=b=c,则应先执行b=c再执行a=(b=c)运算。在编写程序的过程中,希望大家注意其区别,以避免理解错误。

3.强制类型转换运算符

其一般形式为

(类型名)(表达式)

其功能是把表达式的运算结果值强制转换成类型名所表示的数据类型。

例如:

(float)a把a转换为实型数据

(int)(x+y)把x+y的结果转换为整型数据

4.自增、自减运算符

++:增量运算符。其功能是使变量值自增1。

--:减量运算符。其功能是使变量值自减1。

这两个运算符是C语言中所特有的,使用非常方便,其作用就是对变量做加1或减1操作。要注意的是变量在符号前或后,其含义是不同的。

i++ i参与运算后,i的值再自增1。

i-- i参与运算后,i的值再自减1。

粗略地看,++i和i++的作用都相当于i=i+1,但++i和i++的不同之处在于++i先执行i=i+1,再使用i的值;而i++则是先使用i的值,再执行i=i+1。

例如:

若i的值原来为6,则

k=++i k的值为7,i的值也为7

k=i++ k的值为6,但i的值为7

若i的值为3,表达式k=(++i)+(++i)+(++i)的值应该为18,因为++i最先执行,先对表达式进行扫描,进行3次自加(++i),则此时i=6,之后,表达式应该体现为k=6+6+6=18,所以,我们才得出18这个数字。若表达式改为k=(i++)+(i++)+(i++),其最终的k值应该是9,而i最终为6,因此在这个表达式中,先对i进行三次相加,再执行三次i的自加。

【例3-11】

978-7-111-30335-0-Chapter03-33.jpg

printf(“%d\n”,-i--);

}i的初值为10,第2行i加1后输出为11;第3行减1后输出为10;第4行输出i为10

之后再本身加1(此时i为11);第5行输出i为11之后再本身减1(此时i为10);第6行

输出-10之后再加1(此时i为11),第7行输出-11之后再减1(此时i为10)。

3.2.2.3 赋值运算符和赋值表达式

1.赋值运算符

赋值运算符“=”的作用就是将一个数据赋给一个变量。

其一般形式为:

变量=表达式

例如:

c=a+b

w=sin(x)+cos(y)

赋值表达式的作用是将表达式的计算结果值赋给“=”左边的变量。由于赋值运算符具有右结合性。因此

如果有:x=y=z=28

可理解为

x=(y=(z=28))

2.复合的赋值运算符

我们经常会看到一些源程序代码中,出现以下的运算符,这样做的好处是简化了程序,提高了C程序代码的编译效率。

+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=

其效果如下:

978-7-111-30335-0-Chapter03-34.jpg

复合赋值符这种写法,对初学者来说可能不太习惯,但非常有利于编译处理,能提高编译效率并产生高质量的目标代码。

3.2.2.4 逗号运算符和逗号表达式

在C语言中,提供了一种特殊的运算符,它就是逗号运算符——“,”。用逗号运算符将两个表达式连接起来组成一个表达式,称为逗号表达式。

其一般形式为

表达式1,表达式2,表达式3,……表达式n

逗号表达式的求解过程是,从左到右计算出各个表达式的值,而整个逗号运算表达式的值等于最右边那一个表达式的值,就是“表达式n”的值。在大部分情况下,使用逗号表达式的目的只是为了分别得到各个表达式的值,而并不一定要得到使用整个逗号表达式的值。还需要注意的是,并不是程序中任何位置上出现的逗号都认为是逗号运算符。如函数中的参数,同数据类型变量的定义中的逗号只是用来起到间隔作用,而并不是逗号运算符。

【例3-12】

978-7-111-30335-0-Chapter03-35.jpg

例3-12中,y等于整个逗号表达式的值,也就是(b+c)的值,x是第一个表达式的值。对于逗号表达式的使用过程中还请注意以下两点:

1)逗号表达式可以进行嵌套使用。

例如:

表达式1,(表达式2,表达式3)可以把(表达式2,表达式3)看成是一个逗号表达式。

2)程序中使用逗号表达式,通常是要分别求逗号表达式内各表达式的值,但并不一定求得整个逗号表达式的值。

3.2.3 关系运算符和表达式

在程序中我们经常会需要比较两个变量的大小关系,以便做出程序的不同功能选择,我们将比较两个数据量的运算符称为关系运算符。

3.2.3.1 关系运算符及其优先次序

在C语言中有以下关系运算符:

1)<小于

2)<=小于或等于

3)>大于

4)>=大于或等于

5)==等于

6)!=不等于

对于关系运算符,我们同样也并不陌生,C语言中有6种关系运算符,这些运算符的意义看上去也非常直观。或许你从来没用C语言写过程序,那么对前面4个关系运算符一定是再熟悉不过的了。而“==”在VB或PASCAL等语言中是用“=”、“!=”则是用“not”。关系运算符都是双目运算符,其结合性均为左结合。关系运算符的优先级低于算术运算符,高于赋值运算符。在6种关系运算符中,<,<=,>,>=的优先级相同,高于==和!=,==和!=的优先级相同。

3.2.3.2 关系表达式

当两个表达式用关系运算符连接起来时,我们称之为关系表达式。关系表达式通常是用来判别某个条件是否满足。要注意的是用关系运算符的运算结果只有“0”和“1”两种,也就是逻辑的真与假,当指定的条件满足时结果为“1”,不满足时结果为“0”。

关系表达式的一般形式为

表达式 关系运算符 表达式

例如:

a+b>c

a>9

都是合法的关系表达式,由于表达式也可以是关系表达式,因此表达式也允许出现嵌套的情况。例如:

a<(b<c)

x!=(y==z)

关系表达式为“真”时,用“1”表示;关系表达式为“假”时,用“0”表示。

如:

2>1的值为“真”,即为“1”。

(a=3)>(b=5)由于3>5不成立,故其值为假,即为0。

【例3-13】

978-7-111-30335-0-Chapter03-36.jpg

在本例中打印出了各种关系运算符的值。以上部分printf语句中,字符变量是以它相应的ASCII码值参与运算的。对于含多个关系运算符的表达式,如语句中出现“k==j==i+5”的情况,则C编译器会根据运算符的左结合性,先执行k==j,该式不成立,其值为“0”;再执行0==i+5,因为i=2,所以等式也不成立,故表达式值最终的值为“0”。

【单片机C语言设计小实例4】输入两个数,判断两数之间的大小关系。

978-7-111-30335-0-Chapter03-37.jpg

3.2.4 逻辑运算符和表达式

3.2.4.1 逻辑运算符及其优先次序

关系运算符所能反映的是两个表达式之间的大小关系,而逻辑运算符则是用于求条件式的逻辑值,用逻辑运算符将关系表达式或逻辑量连接起来的就是逻辑表达式。也许你会对为什么“逻辑运算符将关系表达式连接起来就是逻辑表达式了”这一描述有疑惑的地方。其实在前面部分我们已经说过,“用关系运算符的运算结果只有“0”和“1”两种,也就是逻辑的真和假”,换句话说也就是逻辑量,而逻辑运算符就用于对逻辑量运算的表达。

C语言中提供了3种逻辑运算符:

1)&&“与”运算

2)||“或”运算

3)!“非”运算

与运算符“&&”和或运算符“||”均为双目运算符,具有左结合特性。非运算符“!”为单目运算符,具有右结合特性。

逻辑运算符和其他运算符优先级的关系如图3-6所示。

“&&”和“||”低于关系运算符,“!”高于算术运算符。

逻辑运算符也有优先级别,从高到低排列,依次如下:!(逻辑非)→&&(逻辑与)→||(逻辑或)。逻辑非的优先级最高,逻辑或的优先级最低。

按照运算符的优先顺序可以得出

978-7-111-30335-0-Chapter03-38.jpg

978-7-111-30335-0-Chapter03-39.jpg

图3-6 运算符优先级

再来看一个例子,如有!True||False&&True

按逻辑运算的优先级别来分析则得到(True代表真,False代表假)

978-7-111-30335-0-Chapter03-40.jpg

3.2.4.2 逻辑运算的值

逻辑运算的结果值分为“真”和“假”两种,用“1”和“0”来表示。其求值规则如下:

1)与运算&&:参与运算的两个量都为真时,结果才为真,否则为假。

例如:

4>0&&9>2

因为4>0为真,9>2也为真,两边同时满足真,所以它们相“与”的结果也为真。

2)或运算||:参与运算的两个量只要有一个为真时,结果就为真。两个量都为假时,结果为假。

例如:

4>0||5>8

虽然5>8为假,但因为4>0为真,所以其最终结果也就为真。

3)非运算!:参与运算量为真时,结果为假;参与运算量为假时,结果为真。

例如:

!(5>0)

其结果为假。

虽然C语言在进行逻辑运算时,以“1”代表“真”,“0”代表“假”,但是否意味着“真”就是“1”,“假”就是“0”呢?其实在C语言中并不是这样的,C语言规定,“0”代表“假”,而以非“0”值代表“真”,这点请大家注意区别。

例如:

由于1和9均为非“0”因此1&&9的值为“真”,即为“1”。

又如:

9||0的值为“真”,即为“1”。

3.2.4.3 逻辑表达式

逻辑表达式的一般形式为

表达式1 逻辑运算符 表达式2

与关系表达式类似,逻辑表达式同样也可以出现嵌套的情况。

例如:

(x&&y)&&z

根据逻辑运算符的左结合性,上面式子等价于

a&&b&&c

逻辑表达式的值就是式子中所有逻辑运算的最后结果值,用“1”和“0”分别代表“真”和“假。

逻辑“与”,就是当条件式1“与”条件式2都为真时,结果为真(非0值),否则为假(0值)。也就是说编译器会先对条件式1进行判断,如果为真(非0值),则继续对条件式2进行判断,当结果为真时,逻辑运算的结果为真(值为1),如果结果为假时,逻辑运算的结果为假(0值)。如果条件式1的逻辑值为假时,那就不用再判断条件式2了,而直接给出运算结果为假,即值为“0”。

逻辑“或”,是指只要两个运算条件中有一个为“真”时,运算结果就为“真”,只有当条件式都为假时,逻辑运算结果才为假。

逻辑“非”,则是把逻辑运算结果值取反,也就是说如果两个条件式的运算值为真,进行逻辑“非”运算后则结果变为假;条件式运算值为假时,那么最后逻辑结果为真。

【例3-14】

978-7-111-30335-0-Chapter03-41.jpg

例3-14中!x和!y的值分别为0,所以!x*!y也为0,故其输出值为0。由于x为非0,故!!!x对0做了三次的逻辑非操作,最终的逻辑值仍为0。对于式子x||i&&j-3,先计算j-3的值为非0,再求i&&j-3的逻辑值为1,因此x||i&&j-3的逻辑值为1。对于式子i<j&&x<y,由于i<j的值为1,而x<y为0,故表达式的值为“1”和“0”相与,最终等于0。对于式子i==5&&c&&(j=8),由于i==5为假,即值为0,该表达式由两个逻辑与运算组成,而当第一个表达式的值为“0”时,就不会再去判断后面的表达式的值了,所以整个表达式的值为0。对于式子x+y||i+j+k由于x+y的值为非0,故整个表达式的值为1。

【单片机C语言设计小实例5】如有!True||False&&True

按逻辑运算的优先级别来分析则得到(True代表真,False代表假)

978-7-111-30335-0-Chapter03-42.jpg

下面我们用程序语言来表达:

978-7-111-30335-0-Chapter03-43.jpg