《第3章 C51语言编程基础ppt课件.ppt》由会员分享,可在线阅读,更多相关《第3章 C51语言编程基础ppt课件.ppt(153页珍藏版)》请在三一办公上搜索。
1、1,第3章 C51语言编程基础与 Keil Vision3开发平台,2,单片机应用系统日趋复杂,对程序的可读性、升级与维护以及模块化要求越来越高,对软件编程要求也越来越高,要求编程人员在短时间内编写出执行效率高、运行可靠的程序代码。同时,也要方便多个编程人员来进行协同开发。C51语言是近年来在8051单片机开发中,普遍使用的程序设计语言,能直接对8051单片机硬件进行操作,既有高级语言特点,又有汇编语言特点,因此在8051单片机程序设计中,得到广泛使用。本章介绍8051单片机的C51语言,以及如何使用C51语言集成化开发平台Keil Vision3,进行C51程序设计与开发。,3.1 C51编
2、程语言简介用于8051单片机编程的C语言,在标准C基础上针对8051硬件特点进行扩展,并向8051上移植,经多年努力,C51已成为公认的高效、简洁的8051单片机的实用高级编程语言。与8051汇编语言相比,C51语言在功能上、结构性、可读性、可维护性上有明显优势,易学易用。3.1.1 C51语言与8051汇编语言比较与8051汇编语言相比,C51有如下优点。(1)可读性好。C51语言程序比汇编语言程序的可读性好,编程效率高,程序便于修改、维护以及升级。,4,(2)模块化开发与资源共享。用C51开发的程序模块可不经修改,直接被其他工程所用,使得开发者能够很好地利用已有的大量标准C程序资源与丰富的
3、库函数,减少重复劳动,同时也有利于多个工程师进行协同开发。(3)可移植性好。为某种型号单片机开发的C语言程序,只需把与硬件相关的头文件和编译链接的参数进行适当修改,就可方便地移植到其他型号的单片机上。例如,为8051单片机编写的程序通过改写头文件以及少量的程序行,就可方便地移植到PIC单片机上。(4)生成的代码效率高。当前较好的C51语言编译系统编译出来的代码效率只比直接使用汇编语言低20%左右,如果使用优化编译选项,最高可达到90%左右。,5,3.1.2 C51语言与标准C语言的比较C51语言与标准C语言间有许多相同地方,但也有自身特点。不同的嵌入式C语言编译系统之所以与标准C语言有不同的地
4、方,主要是由于它们所针对的硬件系统不同。对于8051单片机,目前广泛使用的是C51语言。C51语言基本语法与标准C相同,是在标准C的基础上进行适合8051内核单片机硬件的扩展。深入理解C51语言对标准C语言的扩展部分以及它们的不同之处,是掌握C51语言的关键之一。,6,C51语言与标准C语言一些差别如下。(1)库函数不同。标准C中不适合于嵌入式控制器系统的库函数,被排除在C51语言之外,如字符屏幕和图形函数。有些库函数必须针对8051的硬件特点来做出相应的开发。例如,在标准C中,库函数printf和scanf,常用于屏幕打印和接收字符,而在C51语言中,主要用于串行口数据的收发。(2)数据类型
5、有一定区别。在C51中增加几种8051单片机的数据类型,在标准C的基础上又扩展了4种类型。例如,8051单片机包含位操作空间和丰富的位操作指令,因此,C51语言与标准C语言相比增加了位类型。,7,(3)C51语言变量存储模式与标准C语言中变量存储模式数据不一样。标准C最初是为通用计算机设计的,在通用计算机中只有一个程序和数据统一寻址的内存空间,而C51语言中变量的存储模式与8051单片机的各种存储器区紧密相关。(4)数据存储类型不同。8051存储区可分为内部数据存储区、外部数据存储区以及程序存储区。内部数据存储区可分为3个不同的C51存储类型:data、idata和bdata。外部数据存储区分
6、为2个不同的C51存储类型:xdata和pdata。程序存储区只能读不能写,可能在8051内部或者在外部,C51语言提供的code存储类型用来访问程序存储区。,8,(5)标准C语言没有处理单片机中断的定义,而C51语言中有专门的中断函数。(6)头文件不同。C51语言头文件必须把8051单片机内部的外设硬件资源(如定时器、中断、I/O等)相应的特殊功能寄存器写入到头文件内,而标准C不用。(7)程序结构的差异。由于8051单片机的硬件资源有限,它的编译系统不允许太多的程序嵌套。其次,标准C语言所具备的递归特性不被C51语言支持。,9,但从数据运算操作、程序控制语句以及函数的使用上来说,C51与标准
7、C几乎没有什么明显差别。如果程序设计者具备了有关标准C语言的编程基础,只要注意C51与标准C不同之处,并熟悉8051单片机的硬件结构,就能较快掌握C51编程。3.2 C51语言程序设计基础本节在标准C基础上,了解掌握C51的数据类型和存储类型、C51的基本运算与流程控制语句、C51语言构造数据类型、C51函数以及C51程序设计的其他一些问题,为C51的程序开发打下基础。,10,3.2.1 C51语言中的数据类型与存储类型1数据类型 数据是单片机操作的对象,具有一定格式的数字或数值,数据的不同格式就称为数据类型。Keil C51支持的基本数据类型见表3-1。针对8051的硬件特点,C51在标准C
8、基础上,扩展了4种数据类型(见表3-1中最后4行)。注意,扩展的4种数据类型,不能使用指针来对它们存取。,12,2C51的扩展数据类型 下面对扩展的4种数据类型说明。(1)位变量bit的值可以是1(true),也可是0(false)。(2)特殊功能寄存器sfr。8051单片机的特殊功能寄存器分布在片内数据存储区的地址单元80HFFH之间,“sfr”数据类型占用一个内存单元。利用它可访问8051单片机内部的所有特殊功能寄存器。例如:sfr P1=0 x90这一语句定义了P1端口在片内的寄存器,在程序后续的语句中可以用“P1=0 xff”,使P1的所有引脚输出为高电平的语句来操作特殊功能寄存器。,
9、(3)特殊功能寄存器sfr16。“sfr16”数据类型占用两个内存单元,用于操作占两个字节的特殊功能寄存器。例如:“sfr16 DPTR=0 x82”语句定义了片内16位数据指针寄存器DPTR,其低8位字节地址为82H,高8位字节地址为83H。在程序的后续语句中就可对DPTR进行操作。(4)特殊功能位sbit。sbit是指AT89S51片内特殊功能寄存器的可寻址位。例如:sfrPSW=0 xd0;/定义PSW寄存器地址为0 xd0sbitOV=PSW2;/定义OV位为PSW.2 符号“”前是特殊功能寄存器名字,“”后的数字定义特殊功能寄存器可寻址位在寄存器中的位置,取值必须是07。,14,注意
10、,不要把bit与sbit相混淆。bit定义普通的位变量,只能是二进制的0或1。sbit是定义特殊功能寄存器的可寻址位,值是可以进行位寻址的特殊功能寄存器的某位的绝对地址,例如,PSW寄存器OV位的绝对地址0 xd2。上面的例子还涉及到C51注释的写法问题,C51的注释写法有两种:(1)/,两个斜杠后面跟着的为注释语句,本写法只能注释一行,当换行时,必须在新行上重新写两个斜杠。(2)/*/,一个斜杠与星号结合使用,本写法可注释任一行,即斜杠星号与星号斜杠之间的所有文字都作为注释,即注释有多行时,只需在注释的开始处,加斜杠星号,在注释的结尾处,加上星号斜杠即可。加注释的目的是为了便于读懂程序,所有
11、注释都不参与程序编译,编译器在编译过程中会自动删去注释。,3数据存储类型 在讨论C51数据类型时,须同时提及它的存储类型,以及它与8051单片机存储器结构的关系,因为C51定义的任何数据类型必须以一定的方式,定位在8051单片机的某一存储区中,否则没有任何实际意义。,16,8051有片内、片外数据存储区,还有程序存储区。片内的数据存储区是可读写的,8051的衍生系列最多可有256字节的内部数据存储区(例如AT89S52单片机),其中低128字节可直接寻址,高128字节(80HFFH)只能间接寻址,从地址20H开始的16字节可位寻址。内部数据存储区可分为3个不同的数据存储类型:data、idat
12、a和bdata。访问片外数据存储区比访问片内数据存储区慢,因为访问片外数据存储区要通过对数据指针加载地址来间接寻址访问。C51提供两种不同的数据存储类型xdata和pdata来访问片外数据存储区。,17,程序存储区只能读不能写,可能在8051单片机内部或者外部,或外部和内部都有,由8051单片机硬件决定,C51提供了code存储类型来访问程序存储区。C51存储类型与8051实际的存储空间的对应关系见表3-2。下面对表3-2各种存储区作以说明。(1)DATA区。寻址是最快的,应把常使用的变量放在该区,但该区存储空间有限,DATA区除了包含程序变量外,还包含了堆栈和寄存器组。DATA区声明中的存储
13、类型标识符为data,通常指片内RAM128字节的内部数据存储的变量,可直接寻址。,18,19,声明举例:unsigned char data system_status=0;unsigned int data unit_id8;char data inp_string20;标准变量和用户自声明变量都可存储在DATA区中,只要不超过DATA区的范围即可,由于C51用默认的寄存器组来传递参数,这样DATA区至少失去8字节空间。另外,当内部堆栈溢出的时候,程序会莫名其妙地复位。这是因为8051没有报错机制,堆栈溢出只能以这种方式表示,因此要留有较大的堆栈空间来防止堆栈溢出。(2)BDATA区。DA
14、TA中的位寻址区,在该区中声明变量就可进行位寻址。BDATA区声明中的存储类型标识符为bdata,指的是片内RAM可位寻址的16,20,字节存储区(字节地址为20H2FH)中的128个位。下面是在BDATA区中声明的位变量和使用位变量的例子:unsigned char bdata status_byte;unsigned int bdata status_word;sbit stat_flag=status_byte4;if(status_word15)stat_flag=1;C51编译器不允许在BDATA区中声明float和double型变量。(3)IDATA区。该区使用寄存器作为指针来对片
15、内RAM进行间接寻址,常用来存放使用比较频繁的变量。与外部存储器寻址相比,它的指令执行周期和代码长度相对较短。IDATA区声明中的存储类型标识符为idata,指的是片内RAM的256字节的存储区,只能间接寻址,速度比直接寻址慢。,21,声明举例如下:unsigned char idata system_status=0;unsigned int idata unit_id8;char idata inp_string16;float idata out_value;(4)PDATA区和XDATA区 位于片外存储区,PDATA区和XDATA区声明中的存储类型标识符分别为pdata和xdata。P
16、DATA区只有256字节,仅指定256字节的外部数据存储区。但XDATA区最多可达64KB,对应的xdata存储类型标识符可指定外部数据区64KB内的任何地址。,22,对PDATA区的寻址要比对XDATA区寻址快,因为对PDATA区寻址,只需装入8位地址,而对XDATA区寻址要装入16位地址,所以尽量把外部数据存储在PDATA区中。对PDATA区和XDATA区的声明举例如下:unsigned char xdata system_status=0;unsigned int pdata unit_id8;char xdata inp_string16;float pdata out_value;由
17、于外部数据存储器与外部I/O口是统一编址的,外部数据存储器地址段中除了包含数据存储器地址外,还包含外部I/O口的地址。对外部数据存储器及外部I/O口的寻址将在本章的绝对地址寻址中介绍。,23,(5)程序存储区CODE。程序存储区CODE声明的标识符为code,储存的数据是不可改变的。在C51编译器中可以用存储区类型标识符code来访问程序存储区。声明举例如下:unsigned char code a=0 x00,0 x01,0 x02,0 x03,0 x04,0 x05,0 x06,0 x07,0 x08;上面介绍了C51的数据存储类型,其大小和值域见表3-3。单片机访问片内RAM比访问片外R
18、AM相对快一些,所以应尽量把频繁使用的变量置于片内RAM。即采用data、bdata或idata存储类型,而将容量较大或使用不太频繁的那些变量置于片外RAM,即采用pdata或xdata存储类型。常量只能采用code存储类型。,24,25,变量存储类型定义举例:(1)char data a1;/*字符变量a1被定义为data型,分配在片内RAM低128字节中*/(2)float idata x,y;/*浮点变量x和y被定义为idata型,定位在 片内RAM中,只能用间接寻址方式寻址*/(3)bit bdata p;/*位变量p被定义为bdata型,定位在片内 RAM中的位寻址区*/(4)uns
19、igned int pdata var1;/*无符号整型变量var1定义为pdata型,定位在片外RAM中,相当于Ri间接寻址*/(5)unsigned char xdata a2 4;/*无符号字符型二维数组变量 a24 被定义为xdata存储类型,定位在片外RAM中,占据 24=8字节,相当于DPTR间接寻址*/4数据存储模式 如果在变量定义时略去存储类型标识符,编译器会自动默认存储类型。,26,进一步由SMALL、COMPACT和LARGE存储模式指令限制。例如,若声明char var1,则在使用SMALL存储模式下,var1被定位在data存储区,在使用COMPACT模式下,var1被
20、定位在idata存储区;在LARGE模式下,var1被定位在xdata存储区中。下面对存储模式作进一步说明。(1)SMALL模式。该模式下,所有变量都默认位于8051单片机内部的数据存储器,与使用data指定存储器类型的方式一样。在此模式下,变量访问的效率高,但是所有数据对象和堆栈必须使用内部RAM。(2)COMPACT模式 本模式下所有变量都默认在外部数据存储器的1页(256字节)内,这与,27,使用pdata指定存储器类型是一样的。该类型适用于变量不超过256字节的情况,此限制是由寻址方式决定的,相当于使用数据指针Ri寻址。与SMALL模式相比,该存储模式的效率比较低,对变量访问的速度也慢
21、一些,但比LARGE模式快。(3)LARGE模式 本模式下所有变量都默认位于外部数据存储器,相当于用DPTR寻址。通过数据指针访问外部数据存储器的效率较低,特别是当变量为2字节或更多字节时,该模式要比SMALL和COMPACT产生更多的代码。,在固定的存储器地址上进行变量传递,是C51的标准特征之一。在SMALL模式下,参数传递是在片内数据存储区中完成的。LARGE和COMPACT模式允许参数在外部存储器中传递。C51也支持混合模式。例如,在LARGE模式下,生成的程序可将一些函数放入SMALL模式中,从而加快执行速度。3.2.2 C51语言的特殊功能寄存器及位变量定义 介绍C51如何对特殊功
22、能寄存器及位变量进行定义并访问。1特殊功能寄存器的C51定义 C51语言允许通过使用关键字sfr、sbit或直接引用编译器提供的头文件来对特殊功能寄存器(SFR)进行访问,,29,特殊功能寄存器分布在片内RAM高128字节中,只能采用直接寻址方式。(1)使用关键字定义sfr。为能直接访问特殊功能寄存器SFR,C51提供了一种定义方法,即引入关键字sfr,语法如下:sfr 特殊功能寄存器名字=特殊功能寄存器地址;例如:sfr IE=0 xA8;/中断允许寄存器IE地址A8H sfr TCON=0 x88;/定时器/计数器控制寄存器地址88H sfr SCON=0 x98;/串行口控制寄存器地址9
23、8H在8051中,要访问16位SFR,要用关键字sfr16。16位SFR的低字节地址须作为“sfr16”的定义地址,例如:,30,sfr16 DPTR=0 x82/DPTR 的低8位地址为82H,高8位地址为83H(2)通过头文件访问SFR。各种衍生型的8051单片机的特殊功能寄存器的数量与类型有时是不相同的,对其访问可通过头文件访问来进行。为用户处理方便,C51把8051(或8052单片机)常用的特殊功能寄存器和其中的可寻址位进行了定义,放在一个reg51.h(或reg52.h)的头文件中。当用户要使用时,只需在使用之前用一条预处理命令#include把这个头文件包含到程序中,就可使用特殊功
24、能寄存器名和其中的可寻址位名称了。用户可对头文件进行增减。,头文件引用举例如下:#include/包含8051单片机的头文件void main(void)TL0=0 xf0;/给T0低字节TL0设置时间常数,已在reg51.h中定义TH0=0 x3f;/给T0高字节TH0设置时间常数,已在reg51.h中定义TR0=1;/启动定时器0(3)特殊功能寄存器中的位定义。对SFR中的可寻址位的访问,要使用关键字来定义可寻址位,共3种方法。,32,sbit 位名=特殊功能寄存器位置;例如:sfr PSW=0 xd0;/定义PSW 寄存器的字节地址0 xd0sbit CY=PSW7;/定义CY位为PSW
25、.7,地址为0 xd0sbit OV=PSW2;/定义OV位为PSW.2,地址为0 xd2 sbit 位名=字节地址位置;例如:sbit CY=0 xd07;/CY位地址为0 xd7sbit OV=0 xd02;/OV位地址为0 xd2 sbit 位名=位地址;将位的绝对地址赋给变量,位地址必须在0 x800 xff。例如:sbit CY=0 xd7;/CY位地址为0 xd7sbit OV=0 xd2;/OV位地址为0 xd2,33,【例】AT89S51单片机片内P1口的各寻址位的定义如下:sfr P1=0 x90;sbit P1_7=P17;sbit P1_6=P16;sbit P1_5=P
26、15;sbit P1_4=P14;sbit P1_3=P13;sbit P1_2=P12;sbit P1_1=P11;sbit P1_0=P10;2位变量的C51定义(1)由于8051可位操作,C51扩展的“bit”数据类型用来定义位变量,这是与标准C的不同之处。,34,C51采用关键字“bit”来定义位变量,一般格式为:bit bit_name;例如:bit ov_flag;/将ov_flag定义为位变量 bit lock_pointer;/将lock_pointer定义为位变量(2)函数可以包含类型为bit的参数,也可将其作为返回值。C51程序函数可以包含类型为“bit”的参数,也可将其作
27、为返回值。例如:bit func(bit b0,bit b1);/位变量b0与b1作为函数func的参数 return(b1);/位变量b1作为return函数的返回值,35,(3)位变量定义的限制。位变量不能用来定义指针和数组。例如:bit*ptr;/错误,不能用位变量来定义指针 bit array;/错误,不能用位变量来定义数组array 定义位变量时,允许定义存储类型,位变量都被放入一个位段,此段总是位于8051的片内RAM中,因此其存储类型限制为DATA或IDATA,如果将位变量定义成其他类型,将会导致编译时出错。,36,3.2.3 C51语言的绝对地址访问如何对8051片内RAM、片
28、外RAM及I/O空间进行访问,C51提供两种常用的访问绝对地址的方法。1绝对宏编译器提供了一组宏定义对code、data、pdata和xdata空间进行绝对寻址。程序中用“#include”来对absacc.h中声明的宏来访问绝对地址,包括CBYTE、CWORD、DBYTE、DWORD、XBYTE、XWORD、PBYTE、PWORD,具体使用参见absacc.h头文件。其中:,CBYTE以字节形式对code区寻址;CWORD以字形式对code区寻址;DBYTE以字节形式对data区寻址;DWORD以字形式对data区寻址;XBYTE以字节形式对xdata区寻址;XWORD以字形式对xdata区
29、寻址;PBYTE以字节形式对pdata区寻址;PWORD以字形式对pdata区寻址。,38,【例】片内RAM、片外RAM及I/O定义的程序如下:#include#define PORTA XBYTE0 xFFC0/将PORTA定义为外部I/O口,地址为0 xFFC0,长度8位#define NRAM DBYTE0 x50/将NRAM定义为片内RAM,地址为0 x50,长度8位main()PORTA=0 x3d;/将数据3DH写入地址为0 xffc0的外部I/O端口PORTA中 NRAM=0 x01;/将数据01H写入片内RAM的0 x40单元,39,2 _at_ 关键字关键字 _at_ 可对指
30、定的存储器空间的绝对地址访问,格式如下:存储器类型 数据类型说明符 变量名 _at_ 地址常数其中,存储器类型为C51能识别的数据类型;数据类型为C51支持的数据类型;地址常数用于指定变量的绝对地址,必须位于有效的存储器空间之内;使用 _at_ 定义的变量必须为全局变量。,40,【例】使用关键字 _at_ 实现绝对地址的访问,程序如下:void main(void)data unsigned char y1 _at_ 0 x50;/在data区定义字节变量y1,地址为50H xdata unsigned int y2 _at_ 0 x4000;/在xdata区定义字变量y2,地址为/4000H
31、 y1=0 xff;y2=0 x1234;while(1);【例】将片外RAM 2000H开始的连续20字节清0,程序如下:,41,xdata unsigned char buffer20 _at_ 0 x2000;void main(void)unsigned char i;for(i=0;i20;i+)bufferi=0 如把片内RAM 40H单元开始的8个单元内容清0,程序如下:xdata unsigned char buffer8 _at_ 0 x40;void main(void),42,unsigned char j;for(j=0;j8;j+)bufferj=03.2.4 C51
32、的基本运算与标准C类似,主要包括算术运算、关系运算、逻辑运算、位运算和赋值运算及其表达式等。1算术运算符 算术运算符及说明见表3-4。,43,44,C51中表示加1和减1时可以采用自增运算符和自减运算符,自增和自减运算符是使变量自动加1或减1,自增和自减运算符放在变量前和变量之后是不同的,见表3-5。,45,2逻辑运算符 逻辑运算的结果只有“真”和“假”两种,“1”表示真,“0”表示假。表3-6列出了逻辑运算符及其说明。例如条件“1020”为假,“220)&(26)=0&1=0。3关系运算符 关系运算符是判断两个数之间的关系。说明如表3-7所示。,46,47,4位运算位运算符及其说明见表3-8
33、。,48,在实际应用中,常想改变I/O口中某一位的值,而不影响其他位,如果I/O口可位寻址的,这个问题就很简单。但有时外扩的I/O口只能进行字节操作,要想实现单独位控,就要采用位操作。【例】编程将扩展的某I/O口PORTA(只能字节操作)的PORTA.5清0,PORTA.1置1,程序如下:#define/定义片外I/O口变量PORTA要用该头文件#define PORTA XBYTE0 xffc0/定义一个片外I/O口变量PORTAvoid main()PORTA=(PORTA,49,程序中,第2行定义一个片外I/O口变量PORTA,地址为片外数据存储区的0 xffc0。在main()函数中,
34、“PORTA=(PORTA&0 xdf)0 x02”的作用是先用运算符“&”将PORTA.5置成0,然后再用“0 x02”运算将PORTA.1置为1。5指针和取地址运算符 指针是C51语言中一个十分重要的概念,指针变量用于存储某个变量的地址,C51用“*”和“&”运算符来提取变量内容和变量地址,见表3-9。,50,提取变量的内容和变量的地址的一般形式分别为:目标变量=*指针变量/将指针变量所指的存储单元内容赋值给目标变量指针变量=&目标变量/将目标变量的地址赋值给指针变量例如:a=&b;/取b变量的地址送至变量ac=*b;/把以指针变量b为地址的单元内容送至变量c指针变量中只能存放地址(即指针
35、型数据),不能将非指针类型的数据赋值给指针变量。例如:int i;/定义整型变量iint*b;/定义指向整数的指针变量bb=&i;/将变量i的地址赋给指针变量bb=i;/错,指针变量b只能存放变量指针(变量地址),不能存放变量i的值,51,3.2.5 C51的分支与循环程序结构 C51程序按结构可分为3类,即顺序、分支和循环结构。顺序结构是基本结构,程序自上而下,从main()的函数开始一直到程序结束,只有一条路可走,无其他路径可选,结构较简单和便于理解,这里仅介绍分支结构和循环结构。1分支控制语句分支控制语句有:if语句和switch语句。(1)if语句用来判定所给定的条件是否满足,根据判定
36、结果决定执行两种操作之一。if语句的基本结构如下:if(表达式)语句 括号中的表达式成立时,程序执行大括号内的语句,否则,程序跳过大括号中的语句部分,而直接执行下面的其他语句。C51提供3种形式的if语句:形式1 if(表达式)语句例如:if(xy)max=x;min=y;即如果xy,则x赋给max,y赋给min。如果xy不成立,则不执行大括号中的赋值运算。形式2if(表达式)语句1;else 语句2;,53,例如:if(xy)max=x;else max=y;本形式相当于双分支选择结构。形式3if(表达式1)语句1;else if(表达式2)语句2;else if(表达式3)语句3;else
37、 语句n;,54,例如:if(x100)y=1;else if(x50)y=2;else if(x30)y=3;else if(x20)y=4;else y=5;本形式相当于串行多分支选择结构。在if语句中又含有一个或多个if语句,这称为if语句的嵌套。应当注意if与else的对应关系,else总是与它前面最近的一个if语句相对应。,55,(2)switch语句。if语句只有两个分支可选择,而switch语句是多分支选择语句。switch语句的一般形式如下:switch(表达式1)case 常量表达式1:语句1;break;case 常量表达式2:语句2;break;case 常量表达式n:语
38、句n;break;default:语句n+1;上述switch语句说明如下。,56,(1)每一case常量表达式须互不相同,否则将混乱。(2)各个case和default出现次序,不影响程序执行的结果。(3)switch括号内表达式的值与某case后面的常量表达式的值相同时,就执行它后面的语句,遇到break语句则退出switch语句。若所有的case中的常量表达式的值都没有与switch语句表达式的值相匹配时,就执行default后面的语句。(4)如果在case语句中遗忘了break语句,则程序执行了本行之后,不会按规定退出switch语句,而是将执行后续的case语句。在执行1个case分
39、支后,使流程跳出switch结构,即中止switch语句的执行,可以用1条break语句完成。,switch语句的最后一个分支可以不加break语句,结束后直接退出switch结构。【例】在单片机程序设计中,常用switch语句作为键盘中按键按下的判别,并根据按下键的键号跳向各自的分支处理程序。,58,input:keynum=keyscan()switch(keynum)case 1:key1();break;/如果按下键为1键,则执行函数key1()case 2:key2();break;/如果按下键为2键,则执行函数key2()case 3:key3();break;/如果按下键为3键,
40、则执行函数key3()case 4:key4();break;/如果按下键为4键,则执行函数key4()default:goto input例子中的keyscan()是另行编写的一个键盘扫描函数,如有键按下,该函数就会得到按下键的键值,将键值赋予变量keynum。如果键值为2,则执行键值处理函数key2()后返回;如果键值为4,则执行key4()函数后返回。,59,执行完1个键值处理函数后,则跳出switch语句,从而达到按下不同的按键来进行不同的键值处理的目的。2循环控制语句 许多实用程序都包含循环结构,熟练掌握和运用循环结构的程序设计是C51语言程序设计的基本要求。实现循环结构的语句有以下
41、3种:while语句、do-while语句和for语句。(1)while语句。语法形式为:,while(表达式)循环体语句;表达式是while循环能否继续的条件,如果表达式为真,就重复执行循环体语句;反之,则终止循环体内的语句。while循环结构特点:循环条件测试在循环体开头,要想执行重复操作,首先必须进行循环条件的测试,如条件不成立,则循环体内的重复操作一次也不能执行。,例如:while(P1,do-while语句特点是先执行内嵌的循环体语句,再计算表达式,如表达式的值为非0,则继续执行循环体语句,直到表达式的值为0时结束循环。由do-while构成的循环与while循环的重要区别是:whi
42、le循环的控制出现在循环体之前,只有当while后面表达式的值非0时,才可能执行循环体;在do-while构成的循环中,总是先执行一次循环体,然后再求表达式的值,因此无论表达式的值是0还是非0,循环体至少要被执行一次。在do-while循环体中,要有能使while后表达式的值变为0的操作,否则,循环会无限制地进行下去。根据经验,do-while循环用的并不多,大多数的循环用while来实现会直观。,【例】实型数组sample存有10个采样值,编写程序段,要求返回其平均值(平均值滤波)。程序如下:float avg(float*sample)float sum=0;char n=0;do sum
43、+=samplen;n+;while(n10);return(sum/10);(3)基于for语句的循环。3种循环常用的是for循环。不仅可用于循环次数已知的情况,也可用于循环次数不确定而只给出循环条件情况,完全可替,代while语句。for循环的一般格式为:for(表达式1;表达式2;表达式3)循环体语句;for是关键字,括号中常含有3个表达式,各表达式间用“;”隔开。这3个表达式可以是任意形式的表达式,通常主要用于for循环控制。紧跟在for()之后的循环体,在语法上要求是1条语句;若在循环体内需要多条语句,应用大括号括起来组成复合语句。,for执行过程如下:计算“表达式1”,表达式1通常
44、称为“初值设定表达式”。计算“表达式2”,表达式2通常称为“终值条件表达式”,若满足条件,转下一步,若不满足条件,则转步骤。执行1次for循环体。计算“表达式3”,“表达式3”通常称为“更新表达式”转向步骤。结束循环,执行for循环之后的语句。,下面对for语句的几个特例进行说明。for语句中的小括号内的3个表达式全部为空。例如:for(;)循环体语句;在小括号内只有两分号,无表达式,这意味着没有设初值,无判断条件,循环变量为增值,它的作用相当于while(1),这将导致一个无限循环。一般在编程时,需要无限循环时,可采用这种形式的for循环语句。for语句的3个表达式中,表达式1缺省。,例如:
45、for(;i=100;i+)sum=sum+i;即不对i设初值。for语句的3个表达式中,表达式2缺省。例如:for(i=1;i+)sum=sum+i;即不判断循环条件,认为表达式始终为真,循环将无休止地进行下去。for语句的3个表达式中,表达式1、表达式3省略。例如:for(;i=100;)sum=sum+i;i+;,没有循环体的for语句。例如:int a=1000;for(t=0;ta;t+);本例典型应用就是软件延时。可用循环结构来实现,即循环执行指令,消磨一段已知的时间。指令的执行时间是靠一定数量的时钟周期来计时的,如果使用12MHz晶振,则12个时钟周期花费的时间为1s。,【例】编
46、写一个延时1ms程序。void delayms(unsigned char int j)unsigned char i;while(j-)for(i=0;i125;i+);如把上述程序段编译成汇编代码分析,用for的内部循环大约延时8s,但不是特别精确。不同编译器会产生不同延时,因此i的上限值125应根据实际情况进行补偿调整。,【例】求1+2+3+100的累加和。用for语句编写的程序如下:#include#include main()int nvar1,nsum;for(nvar1=0,nsum=1;nsum=100;nsum+)nVar1+=ncount;/累加求和 while(1);【例
47、】无限循环的结构实现。编写无限循环程序段,可用以下3种结构。,使用while(1)的结构:while(1)代码段;使用for(;)的结构:for(;)代码段;使用do-while(1)的结构:do 代码段;while(1);,3break语句、continue语句和goto语句 在循环体执行中,如满足循环判定条件的情况下跳出代码段,可使用break语句或continue语句;如要从任意地方跳转到代码某地方,可使用goto语句。(1)break语句 循环结构中,可使用break语句跳出本层循环体,马上结束本层循环。【例】执行如下程序段。,【例】执行如下程序段。void main(void)int
48、 i,sum;sum=0;for(i=1;i5)break;print(“sum=%dn”,sum);/*通过串口向计算机屏幕输出显示sum值*/本例如没有break语句,程序将进行10次循环;当i=3 时,sum的值为6,此时,if语句的表达式“sum5”的值为1,于是执行break语句,跳出for循环,从而提前终止循环。,因此在一个循环程序中,既可通过循环语句中的表达式来控制循环是否结束,还可直接通过break语句强行退出循环结构。(2)continue语句作用及用法与break语句类似,区别:当前循环遇到break,是直接结束循环,若遇上continue,则是停止当前这一层循环,然后直接
49、尝试下一层循环。可见,continue并不结束整个循环,而仅仅是中断这一层循环,然后跳到循环条件处,继续下一层的循环。当然,如果跳到循环条件处,发现条件已不成立,那么循环也会结束。,【例】输出整数1100的累加值,但要求跳过所有个位为3的数。为完成题目要求,在循环中加一个判断,如果该数各位是3,就跳过该数不加。如何来判断1100的数中那些数个位是3呢?用求余数的运算符“%”,将一个两位以内的正整数,除以10后,余数是3,就说明这个数的个位为3。例如对于数73,除以10后,余数是3。根据以上分析,参考程序如下:void main(void)int i,sum=0;sum=0;for(i=1;i=
50、100;i+)if(i%10=3)continue;sum=sum+i;,print(“sum=%dn”,sum);/*在计算机屏幕显示sum值,了解本语句的功能 即可*/(3)goto语句 无条件转移语句,当执行goto语句时,将程序指针跳转到goto给出的下一条代码。基本格式如下:goto 标号,【例】计算整数1100的累加值,存放到sum中。void main(void)unsigned char iint sum;sumadd:sum=sum+i;i+;if(i101)goto sumadd;goto语句在C51中经常用于无条件跳转某条必须执行的语句以及在死循环程序中退出循环。为方便阅