C51程序设计完整_第1页
C51程序设计完整_第2页
C51程序设计完整_第3页
C51程序设计完整_第4页
C51程序设计完整_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第1章 C51数据类型与运算1.1 C51数据类型C51的数据类型如下所示:数据类型基本类型位型(bit)字符型(char)整形(int)长整型(long)浮点型(float)双精度浮点型(double)构造类型数组类型(array)结构体类型(struct)共用体型(union)枚举型(enum)指针类型空类型C51编译器支持的数据类型、长度和值域如下表1-1所示。与面向数学运算的计算机相比,51单片机对变量类型或数据类型的选择更具有关键性意义。如果在程序设计中使用大量而不必要的变量类型,这会导致C编译器调用库函数的数量,以处理大量的变量类型和数据类型。所以必须特别慎重地进行变量和数据类型的

2、选择。表1-1 C51的数据类型数据类型长度(bit)长度(Byte)值域范围bitunsigned charsigned charunsigned intsigned intunsigned longsigned longfloatdouble一般指针188161632323264241122444830,10255128127065535327683276704 294 967 2952 147 483 6482 147 483 647±1.176E38±3.40E38(6位数字)±1.176E38±3.40E38(10位数字)存储空间0655351

3、.2 C51数据存储类型C51编译器还可以通过将变量、常量定义成不同的存储类型(data,bdata,idata,pdata,xdata,code)的方法,将它们定义在不同的存储区中。存储类型与51单片机实际存储空间的对应关系如表1-2所示。表1-2 C51存储类型与51单片机实际存储空间的对应关系存储类型与存储空间的对应关系databdataidatapdataxdatacode直接寻址片内数据存储区,访问速度快(128B)可位寻址片内数据存储区,允许位与字节混合访问(16B)间接寻址片内数据存储区,可访问片内全部RAM地址空间(256B)分页寻址片外数据存储区(256B),由MOVX Ri

4、访问寻址片外数据存储区(64KB),由MOVX DPTR访问寻址代码存储区(64KB),由MOVC DPTR访问当使用存储类型data、bdata定义常量和变量时,C51编译器会将它们定位在片内数据存储区中。片内RAM是存放临时性传递变量或使用频率较高变量的理想场所。访问片内数据存储器(data、bdata、idata)比访问片外数据存储器(xdata、pdata)相对快一些,因此可将经常使用的变量置于片内数据存储器,而将规模较大的或不常使用的数据置于片外数据存储器中。C51存储类型及其大小和值域如表1-3所示。例如:表1-3 C51存储类型及其大小和值域存储类型长度(bit)长度(Byte)

5、值域范围dataidatapdataxdatacode888161611122025502550255065 535065 535char data var1; /*字符变量var1被定义为data存储类型,定位在片内RAM中*/bit bdata flags; /*位变量flags被定义为data存储类型,定位在片内RAM中的位寻址区*/ /*(20H2FH)*/float idata x,y,z;/*浮点变量x,y,z被定义为idata存储类型,定位在片内RAM中,并只能用间接寻址的方法进行访问*/unsigned int pdata dimension;/*无符号整型变量dimensio

6、n被定义为pdata存储类型,定位在片外RAM中,并用MOVX Ri访问*/unsigned char xdata vector1044;/*无符号字符三维数组变量vector1044 被定义为xdata存储类型,定位在片外RAM中,占据10×4×4=160个字节空间*/如果在变量定义时省略存储类型标志符,编译器会自动默认存储类型。默认的存储类型进一步由SMALL、COMPACT和LARGE存储模式指令限制,见表1-4所示。存储模式决定了变量的默认存储类型、参数传递区和无明确存储类型的说明。例如,char var1在SMALL存储模式下,var1被定位在data存储区;在C

7、OMPACT模式下,var1被定位在表1-4 存储模式及说明存储模式说明SMALL参数及局部变量放入可直接寻址的片内存储器(最大128B,默认存储类型是data),因此访问十分方便。另外所有对象,包括栈,都必须嵌入片内RAM。栈长很关键,因为实际栈长依赖于不同函数的嵌套层数。COMPACT参数及局部变量放入分页片外存储区(最大256B,默认存储类型是pdata),通过寄存器R0和R1间接寻址,栈空间位于片内数据存储区中。LARGE参数及局部变量放入片外数据存储区(最大64KB,默认存储类型是xdata),使用数据指针DPTR来进行寻址。用此数据指针访问的效率较低,尤其是对两个或多个字节的变量,

8、这种数据类型的访问机制直接影响代码的长度,另一不方便之处在于这种数据指针不能对称操作。pdata存储区,在LARGE模式下,var1被定位在xdata存储区中。1.3 C51定义SFR(特殊功能寄存器)51单片机内有21个特殊功能寄存器(SFR),它们分布在片内RAM的高128B中,对特殊功能寄存器只能用直接寻址方式访问。特殊功能寄存器中还有11个可位寻址的寄存器。在C51中,特殊功能寄存器及其可位寻址的位是通过关键字sfr和sbit来定义的,这种方法与标准C不兼容,只能用于C51。例如:sfrPSW=0xD0;/*定义程序状态字PSW的饿地址为D0H*/sfrTMOD=0x89;/*定义定时

9、器/计数器方式控制寄存器TMOD的地址为89H*/sfrP1=0x90;/*定义P1口的地址为90H*/PSW是可位寻址的SFR,其中各位可用sbit定义。例如:sbitCY=0Xd7;/*定义进位标志CY的地址为D7H*sbitAC=0xD06;/*定义辅助进位标志AC的地址为D6H*/sbitRS0=0XD03;/*定义RS0的地址为D3H*/注意:sfr和sbit只能在函数外使用,一般放在程序的开头。实际上大部分特殊功能寄存器及其可位寻址的位的定义在reg51.h、reg52.h等头文件中已经给出,使用时只需在源文件中包含相应的头文件,即可使用SFR及其可位寻址的位;而对于未定义的位,使

10、用前必须先定义。例如:#include<reg51.h>sbitP10=P10;sbitP12=P12;main() P10=1; P12=0; PSW=0x08; 1.4 C51定义并行口51单片机的基本I/O只有P0、P1、P2和P3四个,除此之外,还可以在片外扩展I/O口和其他功能芯片,它们与外部数据存储器统一编址,即51单片机把它们当做外部数据存储器单元。P0、P1、P2和P3的定义在头文件reg51.h和reg52.h中,扩展的外部RAM单元和外部I/O需要用户自己定义。例如:#include<absacc.h>#define PA XBYTE0xffecMa

11、in() PA=0x3A;/*将数据3AH写入地址为0xffec的存储单元或I/O端口*/以上程序用C的编译预处理命令#define将PA定义为外部I/O口,地址为0xffec,是单字节量。其中XBYTE是一个指针,指向外部数据存储器的零地址单元,它是在头文件absacc.h中定义的。1.5 C51定义位变量51单片机 位运算器,C51相应地设置了位数据类型。1. 位变量的定义位变量用关键字“bit”来定义,它的值是一个二进制位。例如:bit lock;/*将lock定义为位变量*/bit direction;/*将direction定义为位变量*/2. 函数参数和返回值的类型函数可以有bit

12、类型参数,也可以有bit类型的返回值。例如:bit func(bit b0,bit b1) bit a; Return a;使用禁止中断宏命令#progma disable或指定明确的寄存器切换(using n)的函数不能返回位值。3. 对位变量定义的限制不能定义位变量的指针,如:bit *bit_point;不能定义位数组,如:bit bit_array5;位变量说明中可以指定存储类型,位变量的存储类型只能是bdata。在程序设计时,对于可位寻址的对象,既可以字节寻址又可以位寻址的变量,则其存储类型只能是bdata。使用时,先说明字节变量的数据类型和存储类型。例如:int bdata a;/

13、*整型变量a定位在片内数据存储区中的可位寻址区*/char bdata b4;/*字符数组b定位在片内数据存储区中的可位寻址区*/然后,使用sbit关键字定义其中可独立寻址的位变量。例如:sbit a0=a0;/*定义a0为a 的第0位*/sbit a12=a12;/*定义a12为a的第12位*/sbit b03=b03;/*定义b03为b0的第3位*/sbit b36=b36;/*定义b36为b3的第6位*/sbit定义要求基址对象的存储类型为bdata。使用sbit类型位变量时,基址变量和其对应的位变量的说明必须在函数外部进行。1.6 C51运算符、表达式及其规则C51的运算符主要有:算术

14、运算符、关系运算符、逻辑运算符、赋值及复合赋值运算符等等。1. 算术运算符和算术表达式(1) 基本的算术运算符。C51最基本的算术运算符有以下五种:+加法运算符-减法运算符*乘法运算符/除法运算符%模运算或取余运算符对于除法运算符:若两个整数相除,结果为整数(即取整)。对于取余运算符:要求%两侧的操作数均为整型数据,所得结果的符号与左侧操作数的符号相同。(2) 自增、自减运算符。+为自增运算符,-为自减运算符。例如:+j、j+、-i、i-。+和-运算符只能用于变量,不能用于常量和表达式。+j表示先加1,再取值;j+表示先取值,再加1。同理,自减运算也是这个道理。(3) 算术表达式和运算符的优先

15、级与结合性。用算术运算符和括号将运算对象连接起来的式子称为算术表达式。其中的运算对象包括常量、变量、函数、数组、结构等。例如:a+b*c/d。C51规定算术运算符的优先级和结合性为:先乘除模,后加减,括号最优先。如果一个运算符两侧的数据类型不同,则必须通过数据类型转换将数据转换成同种类型。转换方式有以下两种。一是自动类型转换,即在程序编译时,由C编译器自动进行数据类型转换。转换规则如下:charintunsignedlongdoublefloat低高图3-22 数据类型的转换一般来说,当运算对象的数据类型不相同时,先将较低的数据类型转换成较高的数据类型,运算结果为较高的数据类型。二是强制类型转

16、换,使用强制类型转换运算符,其形式为:(类型名)(表达式)。例如:(double)a将a强制转换成double类型(int)(x+y)将x+y强制转换成int类型2. 关系运算符和关系表达式(1) 关系运算符及其优先级。关系运算即比较运算。C51提供了以下六中关系运算符小于小于等于大于大于等于等于!不等于优先级关系是:、这四个运算符的优先级相同,处于高优先级;和!这两个运算符的优先级相同,处于低优先级。关系运算符的优先级低于算术运算符的优先级,而高于赋值运算符的优先级。(2) 关系表达式。用关系运算符将运算对象连接起来的式子称为关系表达式。如:a>b,a+b>=c+d,(a=3)&

17、lt;(b=2)等。关系表达式的值为逻辑值:真和假。C51中用0表示假,用1表示真。例如有关系表达式a>=b,若a的值是4,b的值是3,则给定关系满足,关系表达式的值为1,即逻辑真;若a的值是2,则给定关系不成立,关系表达式的值为0,即逻辑假。3. 逻辑运算符和逻辑表达式(1) 逻辑运算符及其优先级。逻辑运算是对逻辑量进行运算。C51提供三种逻辑运算符。如下:&&逻辑与|逻辑或!逻辑非它们的优先级关系是:!的优先级最高,而且高于算术运算符;|的优先级最低,它低于关系运算符,却高于赋值运算符。(2) 逻辑表达式。用逻辑运算符将运算对象连接起来的式子称为逻辑表达式。运算对象可

18、以是表达式或逻辑量,而表达式可以是算术表达式、关系表达式或逻辑表达式。逻辑表达式的值也是逻辑量,即真或假。对于算术表达式,其值若为0,则认为是逻辑假;若不为0,则认为是逻辑真。逻辑表达式的执行规则是:逻辑表达式不一定完全被执行,只有当一定要执行下一个逻辑运算符才能确定表达式的值时,才执行该运算符。例如:a&&b&&c,若a的值为0,则不需要判断b和c的值就可确定表达式的值为0。又如:a|b|c,若a 值为0,则还需判断b的值,若b的值为1,则不需判断c的值就可确定表达式的值为1。4. 位运算符及其表达式位运算的操作对象只能是整型和字符型数据,不能是实型数据。C5

19、1提供以下六种位运算:&按位与,相当于ANL指令|按位或,相当于ORL指令按位异或,相当于XRL指令按位取反,相当于CPL指令<<左移,相当于RL指令>>右移,相当于RR指令5. 赋值运算符和赋值表达式(1) 赋值运算符。赋值运算符就是赋值号“=”,赋值运算符的优先级低,结合性是右结合性。(2) 赋值表达式。将一个变量与表达式用赋值号连接起来就构成赋值表达式。形式如下:变量名=表达式赋值表达式中表达式包括变量、算术运算表达式、关系运算表达式、逻辑运算表达式等,甚至可以是另一个赋值表达式。赋值过程是将“=”右边表达式的值赋给“=”左边的一个变量,赋值表达式的值就是

20、赋值变量的值。例如:a=b=5,该表达式的值为5a=(b=4)+(c=6),该表达式的值为10(3) 赋值的类型转换规则。在赋值运算中,当“=”两侧的类型不一致时,系统自动将右边表达式的值转换成左边变量的类型,再赋给该变量。转换规则如下: 实型数据赋给整型变量时,舍弃小数部分。 整型数据赋给实型变量时,数值不变,但以浮点数形式存储在变量中。 长字节整型数据赋给短字节整型变量时,实行截断处理。如将long型数据赋给int型变量时,将long型数据的低两字节数据赋给int型变量,而将long型数据的高两字节的数据丢弃。 短字节整型数据赋给长字节整型变量时,进行符号扩展。如将int型数据赋给long

21、型变量时,将int型数据赋给long型变量的低两字节,而将long型变量的高两字节的每一位都设为int型数据的符号值。6. 复合赋值运算符赋值号前加上其他运算符构成复合运算符。C51提供以下十种复合运算符:,*,/,%、&,|,<<,>>。例如:a+=b等价于a=(a+b)x*=a+b等价于x=(x*(a+b)a&=b等价于a=(a&b)a<<=4等价于a=(a<<4)第2章 C51流程控制语句C51程序与其他语言程序一样,程序结构也分为顺序结构、选择结构或分支结构、循环结构三种。由于顺序结构比较简单,在此不多讲述,下面就

22、选择语句和循环语句进行叙述。2.1 选择语句选择语句即条件判断控制语句,它首先判断给定的条件是否满足,然后根据判断的结果决定执行给出的若干种选择之一。C51中选择语句有if语句、switch/case语句。1. if语句C51提供三种形式的if语句:(1) if(表达式)语句;例如: if(p1!=0) c=20;(2) if(表达式)语句1;else语句2;例如:if(p1!=0) c=20;Else c=0;(3) if(表达式1)语句1; else if(表达式2)语句2; else if(表达式3)语句3; else if(表达式n)语句n; else 语句n+1;例如: if(a&g

23、t;=1)c=10; else if(a>=2)c=20; else if(a>=4)c=40; else c=0;(4) if语句的嵌套。在if语句中又含有一个或多个if语句,这种情况称为if语句的嵌套。If语句的嵌套的基本形式如下:外层嵌套if语句if( ) if( )语句1; else 语句2;else内层嵌套if语句If( )语句3;else if( )语句4;内层嵌套if语句请注意if与else的对应关系,else总是与它前面最近的一个if语句相对应最好使内层嵌套的if语句也包含else部分(不要省略),这样,程序中if的数目与else的数目一一对应,不至于出错。另外在编

24、程时最好使用相同深度的缩进排写的形式将同一层次上的if-else语句在同一列的位置上对齐,这样不仅不易出错,也便于阅读程序。例1-1 如图1-1所示,单片机P1口的P1.0和P1.1各接一个开关S1、S2,P1.4、P1.5、P1.6和P1.7各接一只发光二极管。由S1和S2的不同状态来确定哪个发光二极管被点亮。如表1-5所示。图2-1 例1-1附图表1-5 S1、S2与二极管的关系S2S1被点亮的二极管00VD101VD210VD311VD4程序如下:#include<reg51.h>void main() char a; a=P1/*读P1口*/ a=a&0x03;/*

25、屏蔽高6位*/ if(a=0) P1=0x83; else if a=1 P1=0x41; else if a=2 P1=0x22; else P1=0x10;2. switch/case语句switch/case语句是多分支选择语句,它的一般形式如下:switch(表达式)case 常量表达式1:语句1;break;case 常量表达式2:语句2;break;case 常量表达式n:语句n;break;default:语句n+1;(1) 当switch括号中的表达式的值与某一case后面的常量表达式的值相同时,就执行它后面的语句,然后因遇到break而退出switch语句。若所有的case中

26、的常量表达式的值都没有与表达式的值相匹配时,就执行default后面的语句。(2) 每一个case的常量表达式必须时互不相同的,否则将出现混乱局面。(3) 各个case和default出现的次序,不影响程序的执行结果。(4) 如果在case语句中遗忘了break,则程序执行了本行之后,不会按规定退出switch语句,而是执行后续的case语句。例1-2 将例1-1用switch/case语句改写。程序如下:#include “reg51.h”void main()char a;a=P1;/*读P1口*/a=a&0x03;/*屏蔽高6位*/switch (a) case0:P1=0x83

27、;break; case1:P1=0x43;break; case2:P1=0x23;break;case3:P1=0x13; 2.2 循环语句C51循环种类有当型循环和直到型循环,有四种循环实行方法。1. if语句和goto语句goto语句只能构成简单循环,与if语句一起可以实现当型和直到型循环。 构成当型循环loop:if(表达式)语句goto loop; 构成直到型循环loop:语句 if(表达式)goto loop; 例1-3 例1-2的程序只能执行一遍,如果需要在程序执行过程中随时改变开关状态,进而改变二极管的发光状态,就需要不停地执行程序,现用goto语句构成死循环的程序如下:#i

28、nclude “reg51.h”void main() char a; loop:a=P1;a=a&0x03;/*屏蔽高6位*/switch (a)case0:P1=0x83;break;case1:P1=0x43;break;case2:P1=0x23;break;case3:P1=0x13;goto loop;2. while语句while语句用来实现当型循环。其一般格式为:while(表达式)语句表达式可以是任何表达式,语句可以是符合语句。while语句的执行过程:(1) 计算表达式的值;(2) 若其值为非0,则执行内嵌语句(循环),若其值为0,则退出while循环。例1-4 将

29、例1-3的死循环用while循环实现。程序如下:#include “reg51.h”void main()char a;while (1) a=P1; a=a&0x03;/*屏蔽高6位*/ switch (a) case0:P1=0x83;break; case1:P1=0x43;break; case2:P1=0x23;break; case3:P1=0x13; 3. do-while语句do-while语句实现直到型循环。其一般格式为:do 语句 while (表达式);do-while语句的特点是:先执行语句,后判断表达式。执行过程为:(1) 执行内嵌的语句;(2) 计算表达式。

30、当表达式的值为非0时,则循环;当表达式的值为0时,执行do-while语句下面的语句。例1-5 将例1-4用do-while语句改写。程序如下:#include “reg51.h”void main() char a; do a=P1; a=a&0x03;/*屏蔽高6位*/ switch (a) case0:P1=0x83;break;case1:P1=0x43;break;case2:P1=0x23;break;case3:P1=0x13; while (1);4. for语句for语句的一般形式为:for (表达式1;表达式2;表达式3)语句它的执行过程为:(1) 求解表达式1;(

31、2) 求解表达式2,若其值非0,则执行内嵌语句;若其值为0,则退出循环;(3) 求解表达式3,回到第2步;for语句最简单的应用形式是:for(循环变量初值;循环条件;循环变量改变)语句例1-6 求1100的累加和。main()float sum=0;int n;for(n=1;n<=100;n+)sum=sum+(float)n;for语句中,可以没有表达式1、表达式2或表达式3。若三个表达式都没有,则相当于一个死循环。例1-7 将例1-5用for语句改写。程序如下:#include “reg51.h”void main()char a;for (; ;)a=P1;a=a&0x

32、03;/*屏蔽高6位*/switch (a)case0:P1=0x83;break;case1:P1=0x43;break;case2:P1=0x23;break;case3:P1=0x13; 第3章 C51构造数据类型3.1 数组数组是关键数据的有序集合,数组中的每个元素都是统一类型的数据。数组集合用一个名字来标识。数组中元素的顺序用下标表示,下标表示该元素在数组中的位置。下标为n的元素可以表示为数组名n。改变 中的下标就可以访问数组中所有的元素。一个数组元素等同于一个变量,因此又可以说数组是一组相同数据类型的相关变量的有序集合。1. 一维数组由具有一个下标的数组元素组成称为一维数组。(1)

33、 一维数组的定义。一维数组定义的一般形式为:类型说明符 数组名元素个数;数组名是一个标识符,元素个数是一个常量表达式,不能是含有变量的表达式。例如:int a50;/*定义一个数组名为a 的数组,数组包含50个整型的元素/(2) 一维数组的初始化。在定义数组时可以对数组整体初始化,若定义后想对数组赋值,则只能对每个元素分别赋值。例如:int a5=1,2,3,4,5;/*给全部元素赋值,a0=1,a1=2,a2=3,a3=4,a4=5*/int b6=1,2,6;/*给部分元素赋值,b0=1,b1=2,b2=6, b3= b4= b5=0*/int d10;d0=4;d1=-6;/*定义完后再

34、赋值*/2. 二维数组由具有两个下标的数组元素组成的数组称为二维数组。(1) 二维数组的定义。二维数组定义的一般形式是:类型说明符 数组名行数列数;数组名是一个标识符,行数和列数都是常量表达式。例如:float a34;/*a数组有3行4列共12个实型元素*/(2) 二维数组的初始化。与一维数组的初始化相似,定义时可以整体初始化,也可以在定义后单个地进行赋值。例如:int a34=1,2,3,4,5,6,7,8,9,10,11,12;/*全部初始化*/int a34= 1,2,3,4,5,6,7,8,;/*部分初始化,a20=a21=a22=a23=0*/3. 字符数组若一个数组的元素是字符型

35、的,则该数组就是字符数组。(1) 字符数组的定义与赋值。与一维数组的定义赋值的方法类似,例如:char a12=“Chong Qing”;(2) 字符串和字符串结束符。C语言中没有字符串变量,需用字符数组来处理字符串。当数组中存放的实际字符个数与数组的长度不一样时,为了测定字符串的实际长度和使用系统提供的各种字符串函数,C语言规定了字符串结束标志0,它是一个ASCII码值为0的字符。在一个字符数组中,一旦遇到字符0,就表示字符串结束,其后的字符忽略不计。上面a数组的后2个元素皆为0,字符串常量中也自动包含一个字符串结束符0。4. 查表数组的一个很重要的用途就是查表。在单片机应用中,常常要对数学

36、公式进行计算以及对一些传感器的非线性进行补偿,这时,采用查表的办法比较简单有效。因为单片机的计算能力有限,可以将复杂的数学公式或补偿算法事先计算成表格,存入程序存储器中,而这个表格就是数组。3.2 指针关于指针的概念请参考有关资料,下面就C51的指针类型做些说明。C51支持“基于存储器”的指针和“一般” 指针。当定义一个指针变量时,若未指定它所指向的对象的存储器类型,则该指针变量被认为是一般指针;反之若指定了它所指对象的存储类型,则该指针被认为是基于存储器的指针。基于存储器类型的指针由C源代码中指定的存储类型决定,并在编译时确定,这种指针只需12个字节,并且效率高。一般指针需占3个字节:第一个

37、字节为存储器类型的编码(由编译模式的默认值确定),剩余两个字节为地址偏移量。存储器类型决定了对象所用的51单片机存储空间,偏移量指向实际地址。一个“一般”指针可以访问任何变量而不管它存储空间的具体位置。这就允许一般函数,如memcpy()等将数据从任意一个地址拷贝到另一个地址空间。(1) 基于存储器的指针。基于存储器的指针是在说明一个指针时,指定它所指向的对象的存储类型。一般占2字节。例如:char xdata *px;px为指向一个定义在xdata存储器中的字符变量的指针变量。px本身在默认的存储器区域(由编译模式决定),其长度为2字节。前面已经讲到,C51有三种存储模式:即SMALL、CO

38、MPACT和LARGE。C51的存储模式确定函数的参数与局部变量的存储区域。在SMALL模式下,函数的参数与局部变量位于单片机的内部RAM,在COMPACT模式下,函数的参数与局部变量位于单片机外部RAM。例如:char xdata *data py;py为指向一个定义在xdata存储器中的字符变量的指针变量。py本身在RAM中,与编译模式无关,其长度也为2字节。(2) 一般指针。在函数的调用中,函数的指针参数需要用一般指针。一般指针的说明形式如下:char *pz;这里没有指定指针变量pz所指向的变量的存储类型,pz处于编译模式默认的存储区,长度为3字节。3个字节的含义如下: 地址+0+1+

39、2内容存储类型的编码高位地址偏移量低位地址偏移量其中存储类型由编译器决定,不同的存储区域的编码如下:存储类型idataxdatapdatadatacode编码值12345在用常量指针时,如定义外部端口的地址,必须注意正确定义存储类型和偏移量。例如,要将数值0x41写入地址为0x8000外部数据存储器中,可用下列代码实现:#include<absacc.h>XBYTE0x8000=0x41;其中XBYTE是一个指针,它在头文件absacc.h中定义的,定义如下:#define XBYTE (unsigned char *) 0x20000L)XBYTE被定义为(unsigned ch

40、ar *) 0x20000L,它是一个一般指针,其存储类型为2,即为xdata型,偏移量是0000,这样,XBYTE成为指向外部数据存储器的零地址单元的指针。而XBYTE8000则表示外部数据存储器的0x8000单元。C51与标准C语言类似,它的构造类型数据还有结构体(struct)、共用体(union)和枚举(enum)等。请大家参阅有关资料,在此就不再介绍了。第4章 C51函数4.1 函数的定义与分类1. 函数的分类从C语言程序的结构上划分,C语言函数分为主函数main()和普通函数两种。而对于普通函数,从不同的角度或以不同的形式可分为:标准库函数和用户自定义函数。(1) 标准库函数。标准

41、库函数是由C编译系统的库函数提供的,在C便衣系统中将一些独立的功能模块写成公用函数,并将它们集中存放在系统的库函数中,供程序设计时使用。故把这种函数称为标准库函数。C51也提供了比较丰富的库函数资源,将在后面对常用的库函数的功能进行说明。(2) 用户自定义函数。用户自定义函数是用户根据自己的需要而编写的函数。从函数的定义的形式上划分为:无参数函数、有参数函数和空函数。无参数函数:此种函数被调用时,既无参数输入,也不返回结果给调用函数,它是为完成某种操作而编写的。有参数函数:在调用此种函数时,必须提供实际的输入参数,必须说明与实际参数一一对应的形式参数,并在函数结束时返回结果供调用它的函数使用。

42、空函数:此种函数体内无语句,是空白的。调用此种函数时,什么工作也不做。而定义此种函数的目的并不是为了执行某种操作,而是为了给以后程序功能的扩充。2. 函数的定义函数定义的一般形式为:返回值类型 函数名(形式参数列表)函数体例如:int max(int x,int y,int z)返回值的数据类型为整型,函数名为max,x、y、z为个整型入口参数。(1) 关于返回值类型:可以是基本数据类型(int,char,float,double)及指针类型。当函数没有返回值时,用标识符void说明该函数没有返回值。若没有指定返回值类型,默认返回值类型为整型类型。一个函数只有一个返回值,该返回值是通过函数中的

43、return语句获得的。(2) 函数名必须是一个合法的标志符。(3) 形式参数列表包括了函数所需全部参数的定义。此时函数的参数称为形式参数,简称形参。形参可以是基本数据类型的数据、指针类型数据、数组等。在没有调用函数时,函数的形参和函数内部的变量未被分配内存单元,即它们是不存在的。(4) 函数体由两部分组成:函数内部变量定义和函数体其他语句。(5) 各函数的定义是独立的。(6) 函数的定义不能在另一个函数的内部。4.2 函数的调用函数调用的一般形式为:函数名(实际参数列表);在一个函数中需要到某个函数的功能时,就调用该函数。调用者称为主调函数,被调用者称为被调函数。若被调函数是有参函数,则主调

44、函数必须把被雕函数所需的参数传递给被调函数。传递给被调函数的数据称为实际参数,简称实参。若被调函数是无参函数,则调用该函数时,可以没有参数列表,但括号不能省。被调函数执行完后再返回主调函数继续执行剩余程序。实参与形参在熟练数量、类型和顺序上都必须一致;实参可以是常量、变量和表达式;实参对形参的数据传递是单向的,即只能将实参传递给形参。例1-8 编写函数求三个整数中的最大值。int max(int x,int y,int z) int a=x;if(y>x) a=y;if(z>a) a=z;return a; Main()int x1,y1,z1;printf(“Enter 3 nu

45、mbersn”);scanf(“%d%d%d”,&x1,&y1,&z1);printf(“The max is %dn”,max(x1,y1,z1);例中:(1) 函数max有三个形参x、y、z,都是整型参数;函数main有三个实参x1、y1、z1,也都是整型。(2) 变量a是函数max的内部变量,只在函数max内有效。(3) 函数的返回值是通过语句return a;实现的。(4) 一个函数只能返回一个值,可以有多个return语句,但只有一个被执行。(5) 函数的形参和内部变量可以与其它函数的形参和内部变量同名。4.3 函数的嵌套调用与递归调用函数的嵌套调用就是在调用

46、一个函数的过程中,又调用另一个函数。函数的递归调用就是一个函数在其函数体内调用自己。递归调用是一种特殊的循环结构。在C51编程中,递归函数必须是可重入的,可重入的函数必须加关键字reentrant。例1-9 求n!。社设函数f1(n)= n!,则f1(n)可以用如下的递归公式表示: 由递归公式,可以很快得到下面的递归程序:float f1(int n)reentrantif(n=0|n=1)return 1;return n*f1(n-1);main()printf(“%dn”,f1(4);4.4 指向函数的指针变量在把程序调入内存运行时,每一个函数都会被分配了内存单元。将函数的第一条指令所在

47、的地址单元称为该函数的入口地址,可以定义一个指针变量来存放函数的地址,然后通过该指针变量就可以调用此函数。指向函数的指针变量的定义的一般形式:类型说明符(*指针变量名)(参数列表);其中,类型说明符指定了指针所指函数的返回值类型,形参列表指定了指针所指函数的参数个数及类型。例如有如下的函数定义:double max(double x,double y)函数体可以定义指针变量p指向该函数。定义如下: double(*p)(double,double);一旦定义了一个指向某类函数的指针变量后,这个指针变量就只能指向该类函数,即返回值相同、参数的个数、类型、顺序都相同的一类函数,而不能是任意的函数。

48、例1-10 实现一个简单的计算器,能完成加、减、乘、除和取余运算。4.5 C51的库函数C51编译器提供了丰富的库函数,使用这些库函数大大提高了编程效率,用户可以根据需要随时调用。每个库函数都在相应的头文件中给出了函数的原型,使用时只需在源程序的开头用编译预处理命令#include将相关的头文件包含进来即可。下面就一些常用的C51库函数做一些介绍。1. 字符函数库ctype.h(1) extern bit isalpha(char);检查参数字符是否为英文字母,是则返回1,否则返回0。(2) extern bit isalnum(char);检查参数字符是否为英文字母或数字字符,是则返回1,否

49、则返回0。(3) extern bit iscntrl(char);检查参数字符是否为控制符,即ASCII码值为0x000x1f或0x7f的字符,是则返回1,否则返回0。(4) extern bit islower(char);检查参数字符是否为小写英文字母,是则返回1,否则返回0。(5) extern bit isupper(char);检查参数字符是否为大写英文字母,是则返回1,否则返回0。(6) extern bit isdigit(char);检查参数字符是否为数字字符,是则返回1,否则返回0。(7) extern bit isxdigit(char);检查参数字符是否为十六进制数字字

50、符,是则返回1,否则返回0。(8) extern char toint(char);将ASCII字符的09、af(大小写无关)转换为十六进制数。(9) extern char toupper(char);将小写字母转换成大写字母,如果字符不在az之间,则不做转换直接返回该字符。(10) extern char tolower(char);将大写字母转换成小写字母,如果字符不在AZ之间,则不做转换直接返回该字符。2. 标准函数库stdlib.h(1) extern float atof(char*s);将字符串s转换成浮点数值并返回。参数字符串必须包含与浮点数规定相符的数。(2) extern

51、long atof(char*s);将字符串s转换成长整型数值并返回。参数字符串必须包含与长整型数规定相符的数。(3) extern int atof(char*s);将字符串s转换成整型数值并返回。参数字符串必须包含与整型数规定相符的数。(4) void *malloc(usigned int size);返回一块大小为size个字节的连续内存空间的指针。如果返回值为NULL,则无足够的内存空间可用。(5) void free(void *p);释放由malloc函数分配的存储空间。(6) void int_mempool(void *p,unsigned int size);清除由mall

52、oc函数分配的存储器空间。3. 数学函数库math.h(1) extern int abs(int val);extern char abs(int val);extern float abs(int val);extern long abs(int val);计算并返回val的绝对值。这四个函数的区别在于参数和返回值的类型不同。(2) extern float exp(float x);返回以e为底的x的幂,即ex。(3) extern float log(float x);extern float log10(float x);log返回x的自然对数,即lnx;log10返回以10为底的x的对数,即log10x。(4) extern float sprt(float x);返回x的正平方根。(5) extern float sin(float x);extern float cos(float x);extern float tan(float x);sin返回值为sin(x);cos返回值为cos(x);tan返回值为tan(x)。(6) extern float pow(float

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论