1、第2章 Keil C51程序设计基础单片机原理及应用系统设计1、Keil C51系统概述系统概述2、Keil C51软件开发结构软件开发结构3、Keil C51与标准与标准C4、运算符与表达式、运算符与表达式5、C51程序的基本语句程序的基本语句6、Keil C51指针指针7、Keil C51函数函数8、C51代码优化及库函数代码优化及库函数9、C51程序结构及应用要点程序结构及应用要点10、Keil C51高级编程高级编程 Keil C51是一种专门为是一种专门为8051核的单片机设计的高级语核的单片机设计的高级语言言C编译器,支持符合编译器,支持符合ANSI标准的标准的C语言,并针对语言,
2、并针对8051核核单片机作了一些特殊扩展。本章主要介绍单片机作了一些特殊扩展。本章主要介绍C51的基本知识,的基本知识,希望读者能尽快掌握希望读者能尽快掌握C51的编程技术。的编程技术。Keil Keil C51介绍介绍 Keil Keil C51C51是美国是美国Keil SoftwareKeil Software公司出品的公司出品的5151系列兼容单片机系列兼容单片机C C语言软件开发系统,与汇编相比,语言软件开发系统,与汇编相比,C C语言在语言在功能上、结构性、功能上、结构性、可读性、可维护性上可读性、可维护性上有明显的优势,因而易学易用。用过汇有明显的优势,因而易学易用。用过汇编语言
3、后再使用编语言后再使用C C来开发,体会更加深刻来开发,体会更加深刻。Keil Keil C51C51软件提供丰富的库函数和功能强大的集成开发调软件提供丰富的库函数和功能强大的集成开发调试工具,全试工具,全WindowsWindows界面。另外重要的一点,只要看一下编界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到译后生成的汇编代码,就能体会到Keil C51Keil C51生成的目标代码生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。在开发大型软件时更能体现高级语
4、言的优势。2.1 Keil C51系统概述系统概述图2-1 Keil C51软件开发结构框图uVision与与Ishell分别是分别是C51 for Windows和和for Dos的的集成开发环境集成开发环境(IDE),可以完成编辑、编译、连接、调试、,可以完成编辑、编译、连接、调试、仿真等整个开发流程(开发人员可用仿真等整个开发流程(开发人员可用IDE本身或其它编辑器本身或其它编辑器编辑编辑C或汇编源文件)。或汇编源文件)。然后分别由然后分别由C51及及A51编译器编译生成目标文件编译器编译生成目标文件(OBJ)。目标文件可由目标文件可由LIB51创建生成库文件,也可以与库文创建生成库文件
5、,也可以与库文件一起经件一起经L51连接定位生成绝对目标文件连接定位生成绝对目标文件(ABS)。ABS文件由文件由OH51转换成标准的转换成标准的Hex文件,以供调试文件,以供调试器器dScope51或或tScope51使用进行源代码级调试,也可由仿真使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器器使用直接对目标板进行调试,也可以直接写入程序存贮器如如EPROM中。中。(1)C51编译器编译器 Keil C51编译器是一个针对编译器是一个针对80C51系列系列MCU的基于的基于ANSI C标准的标准的C编译器,生成的可执行代码快速、紧凑,在运编译器,生成的
6、可执行代码快速、紧凑,在运行效率和速度上可以和汇编程序得到的代码相媲美。行效率和速度上可以和汇编程序得到的代码相媲美。(2)A51宏汇编器宏汇编器 A51宏汇编器是一个宏汇编器是一个8051核的系列核的系列MCU的宏汇编器,支持的宏汇编器,支持8051及其派生系列的全部指令集。它把汇编语言汇编成机器及其派生系列的全部指令集。它把汇编语言汇编成机器代码。该汇编器允许定义程序中的每一个指令,在需要极快代码。该汇编器允许定义程序中的每一个指令,在需要极快的运行速度,很小代码空间及精确的硬件控制等场合时使用。的运行速度,很小代码空间及精确的硬件控制等场合时使用。A51宏汇编器的宏特性让公共代码只需要开
7、发一次,节约了开宏汇编器的宏特性让公共代码只需要开发一次,节约了开发和维护的时间。发和维护的时间。A51汇编器将源程序汇编成可重定位的目标汇编器将源程序汇编成可重定位的目标代码,并产生一个列表文件。其中可以包含也可以不包含字代码,并产生一个列表文件。其中可以包含也可以不包含字符表及交叉信息。符表及交叉信息。(3)BL51连接连接/定位器定位器BL51连接连接/定位器是具有代码分段功能的连接定位器是具有代码分段功能的连接/定位器,利用从定位器,利用从库中提取的目标模块和由编译器或汇编器生成的一个或多个目库中提取的目标模块和由编译器或汇编器生成的一个或多个目标模块处理外部和全局数据,并将可重定位的
8、段分配到固定的标模块处理外部和全局数据,并将可重定位的段分配到固定的地址上。所产生的一个绝对地址目标模块或文件包含不可重定地址上。所产生的一个绝对地址目标模块或文件包含不可重定位的代码和数据,所有的代码和数据被安置在固定的存储器单位的代码和数据,所有的代码和数据被安置在固定的存储器单元中。该绝对地址目标文件可以:元中。该绝对地址目标文件可以:写入写入EPROM或其他存储器件。或其他存储器件。由由Vision5调试器使用来模拟和调试。调试器使用来模拟和调试。由仿真器用来测试程序。由仿真器用来测试程序。(4)LIB51库管理器库管理器 LIB51库管理器让你建立和维护库文件。库文件是格式化的目库管
9、理器让你建立和维护库文件。库文件是格式化的目标模块(由编译器或汇编器产生)的集合。库文件提供了一标模块(由编译器或汇编器产生)的集合。库文件提供了一个方便的方法来组合及使用大量的连接程序可能用到的目标个方便的方法来组合及使用大量的连接程序可能用到的目标模块。模块。C51编译器与编译器与ANSIC相比,扩展的内容包括数据类型、存储器相比,扩展的内容包括数据类型、存储器类型、存储模式、指针及函数(包括定义函数的重入性、指类型、存储模式、指针及函数(包括定义函数的重入性、指定函数的寄存器组、指定函数的存储模式及定义定函数的寄存器组、指定函数的存储模式及定义中断服务程中断服务程序)。序)。深入理解并应
10、用深入理解并应用C51C51对标准对标准ANSICANSIC的扩展是学习的扩展是学习C51C51的关键之一。的关键之一。因为大多数扩展功能都是直接针对因为大多数扩展功能都是直接针对80518051内核的系列内核的系列CPUCPU硬件。硬件。大致有以下大致有以下8 8类:类:80518051存储类型及存储区域存储类型及存储区域 存储模式存储模式 存储器类型声明存储器类型声明 变量类型声明变量类型声明 位变量与位寻址位变量与位寻址 特殊功能寄存器特殊功能寄存器(SFR)(SFR)C51C51指针指针 函数属性函数属性_at_ _task_ data bdata idataxdata pdata s
11、fr sfr16 alien interrupt small compact large code bit sbit using reentrant 1.程序区域 由由CodeCode说明可有多达说明可有多达64kBytes64kBytes的程序存储器。的程序存储器。2.内部数据存储 内部数据存储器可用以下关键字说明内部数据存储器可用以下关键字说明:datadata:直接寻址区:直接寻址区(00H00H7FH7FH),为内部,为内部RAMRAM的低的低128128字节字节。idataidata:间接寻址区:间接寻址区(00H00HFFHFFH),包括整个内部,包括整个内部RAMRAM区区。bd
12、atabdata:可位寻址区:可位寻址区(20H20H2FH2FH)3.外部数据存储外部外部RAM视使用情况可由以下关键字标识:视使用情况可由以下关键字标识:xdata:可指定多达可指定多达64KB的外部直接寻址区,地址范围的外部直接寻址区,地址范围0000H 0FFFFH pdata:能访问能访问1页页(25bBytes)的外部的外部RAM,主要用于紧凑模,主要用于紧凑模式式(Compact Model)。)。4.特殊功能寄存器存储特殊功能寄存器存储 8051提供提供128Bytes的的SFR寻址区,这区域可位寻址、字节寻址寻址区,这区域可位寻址、字节寻址或或 字寻址,用以控制定时器、计数器
13、、串口、字寻址,用以控制定时器、计数器、串口、I/O及其它部及其它部件,可由以下几种关键字说明:件,可由以下几种关键字说明:sfr:字节寻址字节寻址 比如比如 sfr P0=0 x80;为;为PO口地址为口地址为80H,“”后后HFFH之间的常数。之间的常数。sfr16:字寻址,如字寻址,如sfr16 T2=0 xcc;指定;指定Timer2口地址口地址T2L=0 xcc T2H=0 xCD sbit:位寻址,如位寻址,如sbit EA=0 xAF;指定第指定第0 xAF位为位为EA即中即中断允许。断允许。还可以有如下定义方法:还可以有如下定义方法:sbit OV=PSW2;(定义定义0V为为
14、PSW的第的第2位位)sbit OV0XDO2;(同上同上)或或bit OV-0 xD2。(同上同上)存储模式指定了默认的存储器类型存储模式指定了默认的存储器类型,该类型应用于函,该类型应用于函数参数、局部变量和定义时未包含存储器类型的变量数参数、局部变量和定义时未包含存储器类型的变量。存存储模式决定了没有明确指定存储类型的变量,函数参数等储模式决定了没有明确指定存储类型的变量,函数参数等的缺省存储区域,共三种的缺省存储区域,共三种:1.1.Small模式模式 在此在此模式下所有缺省变量参数均装入内部模式下所有缺省变量参数均装入内部RAM,优点是访,优点是访问速度快,缺点是空间有限,只适用于小
15、程序。问速度快,缺点是空间有限,只适用于小程序。2.2.Compact模式模式 所有所有缺省变量均位于外部缺省变量均位于外部RAM区的一页区的一页(256Bytes)s),具体,具体哪一页可由哪一页可由P2口指定,在口指定,在STARTUP.A51 1文件中说明,也可用文件中说明,也可用pdata指定,优点是空间较指定,优点是空间较Small l为宽裕速度较为宽裕速度较Small慢,较慢,较large要快,是一种中间状态。要快,是一种中间状态。3.3.large模式模式 所有所有缺省变量可放在多达缺省变量可放在多达64KB的外部的外部RAMM区,优点是空区,优点是空间大,可存变量多,缺点是速度
16、较慢。该模式采用数据指针间大,可存变量多,缺点是速度较慢。该模式采用数据指针DPTR来寻址,访问的效率很低。来寻址,访问的效率很低。【备注】(【备注】(1 1)存储模式在)存储模式在C51编译器选项中选择;编译器选项中选择;(2 2)尽可能使用小模式,它产生速度快、效率高的代)尽可能使用小模式,它产生速度快、效率高的代码。码。变量或参数的存储类型可由存储模式指定缺省类型,也变量或参数的存储类型可由存储模式指定缺省类型,也可由关键字直接声明指定可由关键字直接声明指定。各类型分别用:各类型分别用:code,data,idata,xdata,pdata说明说明。例:例:data uar1 char
17、code array“hello!”;unsigned char xdata arr1044;C51提供以下几种扩展数据类型:提供以下几种扩展数据类型:(1)特殊功能寄存器)特殊功能寄存器sfr sfr用于声明字节型(用于声明字节型(8位)特殊功能寄存器;位)特殊功能寄存器;sfr16用于声明字型(用于声明字型(16位)特殊功能寄存器。位)特殊功能寄存器。sfr是是sfr字节地址,其取值范围是字节地址,其取值范围是0255。sfr16是是sfr字地址,其取值范围是字地址,其取值范围是065535。(2)位型)位型bit和和sbit bit是定义位变量数据类型,其取值为是定义位变量数据类型,其取
18、值为0或或1。用于定。用于定义定位在内部义定位在内部RAM的的20H2FH单元的位变量,位地址范围单元的位变量,位地址范围是是007FH,编译器对位地址进行自动分配。,编译器对位地址进行自动分配。sbit是声明位变量的数量类型,其取值为是声明位变量的数量类型,其取值为0或或1。用于。用于声明定位在声明定位在sfr区域的位变量(或位寻址区变量的某确定区域的位变量(或位寻址区变量的某确定位),编译器不自动分配位地址。位),编译器不自动分配位地址。使用时需要注意二者的区别,例如:使用时需要注意二者的区别,例如:bit flag=0;/定义定义flag,位地址由编译器,位地址由编译器007FH范范围分
19、配,并赋初始值围分配,并赋初始值0 sbit var=0 xe6;/声明位变量声明位变量var的位地址为的位地址为0 xe6,“=”含义是声明,不表示赋值。含义是声明,不表示赋值。其余数据类型如:其余数据类型如:char,short,int,long,float等与等与ANSI C相同,如表相同,如表2-1所示。所示。数据类型位数取值范围标准C数据类型字符型signed8-128 127unsigned80 255整型signed16-32768 32767unsigned160 65535长整型signed32-21474883648 21474883647unsigned320 42949
20、67295浮点型float321.75494E-38 3.402823E+38C51扩展数据类型SFR型sfr80 255sfr16160 65535位型bit10,1sbit10,1 1.bit型变量型变量 bit型变量可用变量类型,函数声明、函数返回值等,存贮于型变量可用变量类型,函数声明、函数返回值等,存贮于内部内部RAM的的20H2FH。注意注意:(1)用用pragma disable说明函数和用说明函数和用“usign”指定的函数,不指定的函数,不 能返回能返回bit值值。(2)一个一个bit变量不能声明为指针,如变量不能声明为指针,如bit*ptr;错误;错误。(3)不能有不能有b
21、it数组如:数组如:bit arr5;错误错误。2.可位寻址区说明可位寻址区说明20H2FH可作如下定义可作如下定义:int bdata i;char bdata arr3;然后然后:sbit bit15=I15;sbit arr07=arr07;sbit arr15=arri7;Keil C51对数据有极强的表达能力,具有十分丰富对数据有极强的表达能力,具有十分丰富的运算符,运算符就完成某种特定运算的符号,表达式的运算符,运算符就完成某种特定运算的符号,表达式则是由运算符及运算对象所组成的具有特定含义的一个则是由运算符及运算对象所组成的具有特定含义的一个式子。式子。在任意一个表达式的后面加一
22、个分号在任意一个表达式的后面加一个分号“:”就构成就构成了一个表达式语句了一个表达式语句。由运算符和表达式可以组成。由运算符和表达式可以组成C51程程序的各种语句。序的各种语句。运算符按其在表达式中所起的作用运算符按其在表达式中所起的作用,可分为赋值运,可分为赋值运算符、算术运算符、增量与减量运算符、关系运算符、算符、算术运算符、增量与减量运算符、关系运算符、逻辑运算符、位运算符、复合赋值运算符、逗号运算符、逻辑运算符、位运算符、复合赋值运算符、逗号运算符、条件运算符、指针和地址运算符、强制类型转换运算符。条件运算符、指针和地址运算符、强制类型转换运算符。在在C语言程序中,符号语言程序中,符号
23、“=”称为赋值运算符,它的作用是将称为赋值运算符,它的作用是将一个数据的值赋给一个变量,利用赋值运算符将一个变量与一个数据的值赋给一个变量,利用赋值运算符将一个变量与一个表达式连接起来的式子称为赋值表达式,在赋值表达式一个表达式连接起来的式子称为赋值表达式,在赋值表达式的后面加一个分号的后面加一个分号“;”便构成了赋值语句,赋值语句的格式便构成了赋值语句,赋值语句的格式如下:如下:变量变量=表达式表达式;该语句的意义是先计算出右边的表达式的值,然后将该值赋该语句的意义是先计算出右边的表达式的值,然后将该值赋给左边的变量给左边的变量。上式中的。上式中的“表达式表达式”还可以是一个赋值表达式,还可
24、以是一个赋值表达式,即即C语言允许进行多重赋值。语言允许进行多重赋值。例如例如:x=9;/*将常数将常数9赋给变量赋给变量x*/x=y=8;/*将常数将常数8同时赋给变量同时赋给变量x和和y*/在使用赋值运算符在使用赋值运算符“=”应注意不要与关系运算符应注意不要与关系运算符“=”相混淆相混淆。C语言中的算术运算符有:语言中的算术运算符有:+(加或取正值)运算符、(加或取正值)运算符、(减或取负值)运算符减或取负值)运算符、*(乘)运算符、乘)运算符、/(除)运算符、(除)运算符、%(取余)运算符(取余)运算符。这些运算符中对于加、减和乘法符合一般的运算规则,除这些运算符中对于加、减和乘法符合
25、一般的运算规则,除法有所不同法有所不同:如果是两个整数相除,其结果为整数,舍去小数部分如果是两个整数相除,其结果为整数,舍去小数部分;如如果两个浮点数相除,其结果为浮点数果两个浮点数相除,其结果为浮点数。取余运算要求两个取余运算要求两个运算对象均为整型数据运算对象均为整型数据。算术运算符将运算对象连接起来的式子即为算术表达式算术运算符将运算对象连接起来的式子即为算术表达式。算术运算的一般形式为算术运算的一般形式为:表达式表达式1 算术运算符算术运算符 表达式表达式2例如:例如:x+y/(a-b),(a+b)*(x-y)都是合法的算术表达式都是合法的算术表达式。在求一个算术表达式的值时,要按运算
26、符的优先级在求一个算术表达式的值时,要按运算符的优先级别进行。别进行。算术运算算术运算符中取负值(符中取负值(-)的优先级最高,其次是)的优先级最高,其次是乘法(乘法(*)、除法()、除法(/)和取余()和取余(%)运算符,加法()运算符,加法(+)和减法(和减法()运算符的优先级最低。)运算符的优先级最低。需要需要时可在算术表达式中采用圆括号来改变运算符时可在算术表达式中采用圆括号来改变运算符的优先级,括号的优先级最高。的优先级,括号的优先级最高。C语言中除了基本的加、减、乘、除运算之外,还语言中除了基本的加、减、乘、除运算之外,还提供两种特殊的运算符:提供两种特殊的运算符:+(增量)运算符
27、和(增量)运算符和-(减量)(减量)运算符。运算符。增量和减量是增量和减量是C51中特有一种运算符,它们的作用分中特有一种运算符,它们的作用分别是对运算对象做加别是对运算对象做加1和减和减1运算。运算。例如例如:+i,i+,-j,j-等等。增量运算符和减量运算符只能用于变量,不能用于常增量运算符和减量运算符只能用于变量,不能用于常数或表达式,在使用中要注意运算符的位置数或表达式,在使用中要注意运算符的位置。例如,。例如,+i与与i+的意义完全不同,前者为在使用的意义完全不同,前者为在使用i之前先对之前先对i的的值加值加1,而后者则是在使用,而后者则是在使用i之后再对之后再对i的值加的值加1。C
28、语言中有语言中有6中关系运算符:中关系运算符:(大于大于)、)、=(大于等于大于等于)、)、y,x+yz,(,(x=3)(y=4)都是合法的关系)都是合法的关系表达式。表达式。关系运算符通常用来判别某个条件是否满足关系运算符通常用来判别某个条件是否满足,关系运算,关系运算的结果只有的结果只有0和和1两种值。当所指定的条件满足时结果为两种值。当所指定的条件满足时结果为1,条,条件不满足时结果为件不满足时结果为0。C语言中有语言中有3种逻辑运算符:种逻辑运算符:|(逻辑或)、(逻辑或)、&(逻辑与)、(逻辑与)、!(逻辑非逻辑非)。)。逻辑运算符用来求某个条件式的逻辑值,用逻辑运算符将关系逻辑运算
29、符用来求某个条件式的逻辑值,用逻辑运算符将关系表达式或逻辑量连接起来就是逻辑表达式表达式或逻辑量连接起来就是逻辑表达式。逻辑运算的一般形式为逻辑运算的一般形式为:逻辑与逻辑与 条件式条件式1&条件式条件式2逻辑或逻辑或 条件式条件式1|条件式条件式2逻辑非逻辑非 !条件式条件式 例如:例如:x&y,a|b,!z都是合法的逻辑表达式都是合法的逻辑表达式。进行逻辑与运算时进行逻辑与运算时,首先对条件式,首先对条件式1进行判断,如果结果为进行判断,如果结果为真(非真(非0值),则继续对条件式值),则继续对条件式2进行判断,当结果也为真时,进行判断,当结果也为真时,表示逻辑运算结果为真(值为表示逻辑运
30、算结果为真(值为1);反之,如果条件式);反之,如果条件式1的结果的结果为假,则不再判断条件式为假,则不再判断条件式2,而直接给出逻辑运算的结果为假,而直接给出逻辑运算的结果为假(值为(值为0)。)。进行进行逻辑或运算时,只要两个条件式中有一个为真,逻辑或运算时,只要两个条件式中有一个为真,逻辑运算的结果便为真(值为逻辑运算的结果便为真(值为1),只有当条件式),只有当条件式1和条和条件式件式2均不成立时,逻辑运算的结果才为假(值为均不成立时,逻辑运算的结果才为假(值为0)。)。进行进行逻辑非运算时,对条件式的逻辑值直接取反。逻辑非运算时,对条件式的逻辑值直接取反。与与关系运算符类似,逻辑运算
31、符通常用来判别某个关系运算符类似,逻辑运算符通常用来判别某个逻辑条件是否满足,逻辑运算的结果只有逻辑条件是否满足,逻辑运算的结果只有0和和1两种值。两种值。上面上面几种运算符的优先级为(由高至低):逻辑非几种运算符的优先级为(由高至低):逻辑非算术运算符算术运算符关系运算符关系运算符逻辑与逻辑与逻辑或。逻辑或。2.4.6 2.4.6 位位运算符运算符 C能对运算对象进行按位操作是能对运算对象进行按位操作是C语言的一大特点,使之能对语言的一大特点,使之能对计算机的硬件直接进行操作、语言中共有计算机的硬件直接进行操作、语言中共有6种位运算符:种位运算符:(按位取反按位取反)(左移左移)(右移右移)
32、&(按位与按位与)位异或位异或 )|(按位或按位或)位运算的一般形式如下位运算的一般形式如下:变量变量1 位运算符位运算符 变量变量2 位位运算符的作用是按位对变量进行运算,并不改变参与运运算符的作用是按位对变量进行运算,并不改变参与运算的变量的值。若希望按位改变变量的值,则应采用相应的赋算的变量的值。若希望按位改变变量的值,则应采用相应的赋值运算。另外位运算符不能用来对浮点型数据进行操作,例如,值运算。另外位运算符不能用来对浮点型数据进行操作,例如,先用赋值语句先用赋值语句a=0 xEA;将变量;将变量a赋值为赋值为0 xEA,接着对变量,接着对变量a进进行移位操作行移位操作a2,其结果是将
33、十六进制数,其结果是将十六进制数0 xEA左移左移2位,移空位,移空的的2位补位补0,移出的,移出的2位丢弃,移位的结果为位丢弃,移位的结果为0 xa8,而变量,而变量a的值的值在执行后仍为在执行后仍为0 xEA。如果希望变量如果希望变量a在执行之后为移位操作的结果,则应采用语句为:在执行之后为移位操作的结果,则应采用语句为:a=a2。位位运算符的优先级从高到低依次是:按位取反(运算符的优先级从高到低依次是:按位取反()左移左移()按位与(按位与(&)按位异或(按位异或()按位或按位或(|)。)。2.4.7 2.4.7 复合复合赋值运算符赋值运算符 在赋值运算符在赋值运算符“=”的前面加上其他
34、运算符,就构成了所谓的前面加上其他运算符,就构成了所谓复合赋值运算符,复合赋值运算符,C语言中共有语言中共有10种赋值复合运算符:种赋值复合运算符:+=(加加法赋值法赋值)、)、-=(减法赋值减法赋值)、)、*=(乘法赋值乘法赋值)、)、/=(除法赋值除法赋值)、%=(取模赋值取模赋值)、)、=(右移位赋值右移位赋值)、&=(逻辑与赋值逻辑与赋值)、)、|=(逻辑或赋值逻辑或赋值)、)、=(逻辑异或赋逻辑异或赋值值)、)、=(逻辑非赋值逻辑非赋值)。)。复合赋值运算首先对变量进行某种运算复合赋值运算首先对变量进行某种运算,然后将运算的结,然后将运算的结果再赋值给该变量果再赋值给该变量。复合运算
35、的一般形式为复合运算的一般形式为:变量变量 复合赋值运算符复合赋值运算符 表达式表达式例如例如:a+=3等价于等价于a=a+3;x*=y+8等价于等价于x=x*(y+8)。)。采用复合赋值运算符,可以使程序简化,同时还可以提高程序采用复合赋值运算符,可以使程序简化,同时还可以提高程序的编译效率的编译效率。2.4.8 2.4.8 逗号逗号运算符运算符 C语言中的逗号语言中的逗号“,”是一个特殊的运算符,可以用它将两是一个特殊的运算符,可以用它将两个(或多个)表达式连接起来,称为逗号表达式。个(或多个)表达式连接起来,称为逗号表达式。逗号表达式的一般形式为逗号表达式的一般形式为:表达式表达式1,表
36、达式,表达式2,表达式表达式n 程序运行时对于逗号表达式的处理程序运行时对于逗号表达式的处理,是从左至右依次计算,是从左至右依次计算出各个表达式的值,而整个逗号表达式的值是最右边表达式出各个表达式的值,而整个逗号表达式的值是最右边表达式(即表达式(即表达式n)的值)的值。在许多情况下在许多情况下,使用逗号表达式的目的只是为了分别得到,使用逗号表达式的目的只是为了分别得到各个表达式的值,而并不一定要得到和使用逗号表达式的值各个表达式的值,而并不一定要得到和使用逗号表达式的值。另外还要注意,并不是在程序的任何地方出现的逗号,都可以另外还要注意,并不是在程序的任何地方出现的逗号,都可以认为是逗号运算
37、符认为是逗号运算符。有些函数中的参数也是用逗号来间隔的,。有些函数中的参数也是用逗号来间隔的,例如,库输出函数例如,库输出函数printf(“n%d%d%d”,a,b,c)中的)中的“a,b,c”是函数的三个参数,而不是一个逗号表达式。是函数的三个参数,而不是一个逗号表达式。条件运算符条件运算符“?:?:”是是C语言中唯一的一个三目运算符,它语言中唯一的一个三目运算符,它要求有要求有3个运算对象,用它可以将个运算对象,用它可以将3个表达式连接构成一个个表达式连接构成一个条件表达式。条件表达式。条件表达式的一般形式如下条件表达式的一般形式如下:逻辑表达式逻辑表达式?表达式?表达式1:表达式:表达
38、式2 其功能是首先计算逻辑表达式,当值为真(非其功能是首先计算逻辑表达式,当值为真(非0值)时,将值)时,将表达式一的值作为整个条件表达式的值:当逻辑表达式的表达式一的值作为整个条件表达式的值:当逻辑表达式的值为假(值为假(0值)时,将表达式值)时,将表达式2的值作为整个表达式的值。的值作为整个表达式的值。例如,条件表达式例如,条件表达式max=(ab)?ab的执行结果是将的执行结果是将a和和b中较中较大者赋值给变量大者赋值给变量max。另外,条件表达式中逻辑表达式的类。另外,条件表达式中逻辑表达式的类型可以与表达式型可以与表达式1和表达式和表达式2的类型不一样。的类型不一样。2.4.2.4.
39、10 10 指针指针和地址运算符和地址运算符 指针是指针是C语言中的最重要的概念,也是最难理解和掌握的。语言中的最重要的概念,也是最难理解和掌握的。C语言中专门规定了一种指针类型的数据。语言中专门规定了一种指针类型的数据。变量的指针就是该变变量的指针就是该变量的地址,还可以定义一个指向某个变量的指针变量量的地址,还可以定义一个指向某个变量的指针变量。为了表。为了表示指针变量和它所指向的变量地址之间的关系,示指针变量和它所指向的变量地址之间的关系,C语言提供两语言提供两个专门的运算符:个专门的运算符:*(取内容)和取内容)和&(取地址取地址)。取内容和取地址的一般形式为取内容和取地址的一般形式为
40、:变量变量=*指针变量指针变量 指针变量指针变量=&目标变量目标变量 取取内容运算的含义是将指针变量所指向的目标变量的值赋内容运算的含义是将指针变量所指向的目标变量的值赋给左边的变量;取地址运算的含义是将目标变量的地址赋给左给左边的变量;取地址运算的含义是将目标变量的地址赋给左边的变量。需要注意的是,指针变量中只能存放地址(即指针边的变量。需要注意的是,指针变量中只能存放地址(即指针型数据),不要将一个非指针类型的数据赋值给一个指针变量。型数据),不要将一个非指针类型的数据赋值给一个指针变量。例如,下面的语句完成了对指针变量赋值(地址值):例如,下面的语句完成了对指针变量赋值(地址值):cha
41、r data *p;/*定义指针变量定义指针变量*/=30H;/*给指针变量赋值,给指针变量赋值,30H为片内为片内RAM地址地址*/C51提供了一种对存储器地址进行访问的方法,即利用库函数提供了一种对存储器地址进行访问的方法,即利用库函数中的绝对地址访问头文件中的绝对地址访问头文件absacc.h来访问不同区域的存储器和来访问不同区域的存储器和片外扩展片外扩展I/O端口。端口。在在absacc.h头文件中进行了如下宏定义头文件中进行了如下宏定义:CBYTE(地址)(地址)(访问访问CODE区区char型型)DBYTE(地址)(地址)(访问访问DATA区区char型型)PBYTE(地址)(地址
42、)(访问(访问PDATA区或区或I/O端口端口char型型)XBYTE(地址)(地址)(访问访问XDATA区或区或I/O端口端口char型型)CWORD(地址)(地址)(访问访问CODE区区int型型)DWORD(地址)(地址)(访问访问DATA区区int型型)PWORD(地址)(地址)(访问(访问PDATA区或区或I/O端口端口int型型)XWORD(地址)(地址)(访问(访问XDATA区或区或I/O端口端口int型型)下面语句向片内外扩展端口地址下面语句向片内外扩展端口地址7FFFH写入一个字符型数据:写入一个字符型数据:XBYTE0 x7FF=0 x9988;如果采用如下语句定义一个如果
43、采用如下语句定义一个D/A转换器端口地址:转换器端口地址:#define DAC0832 XBYTE(0 x7FFF);那么程序文件中所出现那么程序文件中所出现DAC0832的地方,就是对地址为的地方,就是对地址为0 x7FFF的外部的外部RAM单元(单元(I/O端口)进行访问。端口)进行访问。8051核单片机具有核单片机具有100多个品种,为了方便访问不同品种单多个品种,为了方便访问不同品种单片机内部特殊功能寄存器,片机内部特殊功能寄存器,C51提供了多个相关头文件,如提供了多个相关头文件,如reg51.h、reg52.h等,在头文件中对单片机内部特殊功能寄存等,在头文件中对单片机内部特殊功
44、能寄存器及其有位名称的可寻地址进行了定义,编程时只要根据所器及其有位名称的可寻地址进行了定义,编程时只要根据所采用的单片机,在程序文件开始处使用文件包含处理命令采用的单片机,在程序文件开始处使用文件包含处理命令“#include”将相关头文件包含进来,就可以直接引用特殊功将相关头文件包含进来,就可以直接引用特殊功能寄存器(注意必须采用大写字母)。能寄存器(注意必须采用大写字母)。例如,下面语句完成了例如,下面语句完成了8051定时方式寄存器定时方式寄存器TM0D的赋值:的赋值:#include TMOD=0 x20;C语言中的圆括号语言中的圆括号“()()”也可作为一种运算符使用,这就是强也可
45、作为一种运算符使用,这就是强制类型转换运算符,它的作用是将表达式或变量的类型强制转制类型转换运算符,它的作用是将表达式或变量的类型强制转换为所指定的类型换为所指定的类型。在在C51程序中进行算式运算时需要注意数据类型的转换,数据程序中进行算式运算时需要注意数据类型的转换,数据类型转换分为隐式转换和显式转换。隐式转换是在对程序进行类型转换分为隐式转换和显式转换。隐式转换是在对程序进行编译时由编译器自动处理的,并且只有几本数据类型(即编译时由编译器自动处理的,并且只有几本数据类型(即char、int、long和和float)可以进行隐式转换。)可以进行隐式转换。其他数据类型不能进行其他数据类型不能
46、进行隐式转换隐式转换。例如,我们不能把一个整型数利用隐式转换赋值给一个例如,我们不能把一个整型数利用隐式转换赋值给一个指针变量,在这种情况下就必须利用强制类型转换运算指针变量,在这种情况下就必须利用强制类型转换运算符来进行显式转换符来进行显式转换。强制类型转换运算符的一般使用形式为强制类型转换运算符的一般使用形式为:(类型类型)=表达式表达式强制类型转换在给指针变量赋值时特别有用强制类型转换在给指针变量赋值时特别有用。例如,预。例如,预先在单片机的片外数据存储器(先在单片机的片外数据存储器(xdata)中定义了一个字)中定义了一个字符型指针变量符型指针变量px,如果想给这个指针变量赋一个初值,
47、如果想给这个指针变量赋一个初值0 xB000,可以写成:,可以写成:px=(char xdata*)0 xB000,这种方法,这种方法特别适合于标识符来存取绝对地址。特别适合于标识符来存取绝对地址。C语言中提供了一种用于求取数据类型、变量及表达式的字节语言中提供了一种用于求取数据类型、变量及表达式的字节数的运算符:数的运算符:sizeof。该运算符的一般适用形式为该运算符的一般适用形式为:sizeof(表达式)或(表达式)或sizeof(数据类型(数据类型)应该注意的是,应该注意的是,sizeof是一种特殊的运算符,不要错误地认为是一种特殊的运算符,不要错误地认为它是一个函数它是一个函数。实际
48、上,字节数的计算在程序编译时就完成了,实际上,字节数的计算在程序编译时就完成了,而不是在程序执行的过程中才计算出来而不是在程序执行的过程中才计算出来。2.5.1 表达式语句表达式语句C语言提供了十分丰富的程序控制语句,表达式语句是最基本语言提供了十分丰富的程序控制语句,表达式语句是最基本的一种语句。的一种语句。在表达式的后边加一个分好在表达式的后边加一个分好“:”就构成了表达式就构成了表达式语句语句。下面的语句都是合法的表达式语句下面的语句都是合法的表达式语句:a=+b*9;x=8;y=7;z=(x+y)/a;+i;表达式语句也可以仅由一个分号表达式语句也可以仅由一个分号“;”组成,这种语句成
49、为空语组成,这种语句成为空语句句。空语句在程序设计中有时是很有用的,当程序在语法上需空语句在程序设计中有时是很有用的,当程序在语法上需要有一个语句,但在语义上并不要求有具体的动作时,便可以要有一个语句,但在语义上并不要求有具体的动作时,便可以采用空语句采用空语句。空语句通常有以下两种用法。空语句通常有以下两种用法。(1)在程序中为有关语句提供标号,用以标记程序执行)在程序中为有关语句提供标号,用以标记程序执行的位置。例如,采用下面的语句可以构成一个循环。的位置。例如,采用下面的语句可以构成一个循环。repeat:;goto repeat;(2)在用)在用while语句构成的循环语句后面加一个分
50、号,语句构成的循环语句后面加一个分号,形成一个不执行其他操作的空循环体。这种空语句在等待形成一个不执行其他操作的空循环体。这种空语句在等待某个事件发生时特别有用。例如,下面这段程序是读取某个事件发生时特别有用。例如,下面这段程序是读取8051单片机串行口数据的函数,其中就用了一个空语句单片机串行口数据的函数,其中就用了一个空语句while(!RI),来等待单片机串行口接受结束。),来等待单片机串行口接受结束。#include /*插入插入8051单片机的预定义文件单片机的预定义文件*/char _getkey()/*函数定义函数定义*/*函数体开始函数体开始*/char c;/*定义变量定义变