第5章 C语言程序的基本结构Word格式文档下载.docx
《第5章 C语言程序的基本结构Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《第5章 C语言程序的基本结构Word格式文档下载.docx(17页珍藏版)》请在冰豆网上搜索。
P1=0x55;
//20
}//21
/*=====================22============*/
voidmain(void)//23
{//24
while
(1)//25
{//26
light1();
//27
delay();
//28
light2();
//29
//30
}//31
}//32
编译通过后,试验板接通5V稳压电源,将生成的CS5-1.hex文件下载到51MCUDEMO试验板上的单片机89S51中,注意,标示“LED”的双排针应插上8个短路块。
我们看到D0~D7这8个二极管中的D0、D2、D4、D6及D1、D3、D5、D7的交替点亮,周期差不多为1S。
图5-1为keilC51软件进行仿真时的界面。
图5-1keilc51软件进行仿真时的界面
5.1.2程序分析解释
序号1:
包含头文件REG51.H。
序号2:
程序分隔或注释,在“/*”及“*/”之间的内容,程序不会去处理,因此通常可进行文字注释,能增加程序的可读性,当然也可作为程序语句模块之间的分隔。
序号3:
定义函数名为delay的延时子函数。
序号4:
delay延时子函数开始。
序号5:
定义两个无符号整形变量i,j。
序号6~10:
两个for语句循环体,作用是延时,由于我们还未学习for语句,因此这里可暂不理会。
序号11:
delay的延时子函数结束。
序号12:
程序分隔或注释,在“//”之后的内容,程序也不会去处理,因此也可进行文字注释,能增加程序的可读性,当然也能作为程序模块之间的分隔。
但应注意,这种风格的注释,只对本行有效,所以在只需要一行注释的时候,往往采用“//……”这种格式。
而“/*……*/”风格的注释,既可用于一行,也可用于多行。
序号13:
定义函数名为light1的子函数,该子函数用于点亮D0、D2、D4、D6四个LED。
序号14:
light1子函数开始。
序号15:
向P1口送数0xaa(0xaa为16进制数,相当于汇编语言中的AAH,前缀加0x为C51的风格),这条语句的目的是点亮D0、D2、D4、D6四个LED。
序号16:
light1子函数结束。
序号17:
程序分隔。
序号18:
定义函数名为light2的子函数,该子函数用于点亮D1、D3、D5、D7四个LED。
序号19:
light2子函数开始。
序号20:
向P1口送数0x55,目的是点亮D1、D3、D5、D7四个LED。
序号21:
light2子函数结束。
序号22:
序号23:
定义函数名为main的主函数。
序号24:
main的主函数开始。
序号25:
while循环语句,这里进行无限循环。
序号26:
while循环语句开始。
序号27:
调用light1子函数模块。
序号28:
调用延时子函数模块。
序号29:
调用light2子函数模块。
序号30:
序号31:
while循环语句结束。
序号32:
main的主函数结束。
5.2C语言程序的组成结构
从上面的程序我们可以看出,C语言程序的组成结构如下:
预处理命令include<
>
功能子函数1delay()
{
函数体…
}
功能子函数2light1()
┊
功能子函数nlight2()
主函数main()
主函数体…
}
结论:
C语言程序是由函数构成的,一个C源程序至少包括一个函数(主函数),一个C源程序有且只有一个名为main()的函数,也可能包含其它函数,因此,函数是C程序的基本单位。
函数后面一定有一对大括号“{……}”,在大括号里书写程序。
C语言程序总是从main主函数开始执行的,而不管物理位置上这个main()放在什么地方。
主函数通过直接书写语句和调用其它功能子函数来实现有关功能,这些功能子函数可以是由C语言本身提供给我们的库函数,也可以是用户自己编写的函数。
那么库函数和用户自定义子函数有什么区别呢?
简单地说,库函数就是针对一些经常使用的算法,经前人开发、归纳、整理形成的通用功能子函数集供大家使用。
而自己编写的功能子函数则称用户自定义功能子函数,显然,用户自定义功能子函数是用户根据自己需要而编写的。
可以看出,使用C语言开发产品,可以大量使用库函数而减少用户自己编写程序的工作量,这样,产品开发的速度和质量是汇编语言绝对不能相比的。
Keil
C51内部有数百个库函数可供我们使用。
调用Keil
C51的库函数时只需要包含具有该函数说明的相应的头文件即可。
5.3只用主函数完成LED1~LED8这8个二极管实现D0、D2、D4、D6及D1、D3、D5、D7的交替点亮的实验,周期约1秒
5.3.1源程序文件
在D盘中建立一个文件目录(CS5-2),然后建立CS5-2.uv2的工程项目,最后建立源程序文件(CS5-2.c)。
/*=====================2============*/
voidmain(void)//3
{unsignedinti,j;
//4
while
(1)//5
{//6
P1=0xaa;
//7
//==================8
i++)//9
{//10
j++)//11
}//12
}//13
//==================14
P1=0x55;
//15
//==================16
i++)//17
{//18
120;
j++)//19
}//20
}//21
//==================22
}//23
}//24
编译通过后,试验板接通5V稳压电源,将生成的CS5-2.hex文件下载到51MCUDEMO试验板上的单片机AT89S51中,注意,标示“LED”的双排针应插上8个短路块,发现实验效果与CS5-1完全相同。
但是我们能看出,此程序的结构条理没有CS5-1的程序来得清晰,一大堆实现各种功能的语句全部放在主函数内,显得有点乱。
因此一个设计合理的C语言程序,不仅语句要流畅,而且结构也要简洁明了。
5.3.2程序分析解释
主函数开始。
while循环语句,进行无限循环。
序号6:
序号7:
向P0口送数0xaa,目的是点亮D0、D2、D4、D6四个LED。
序号8:
程序语句分隔。
序号9~13:
两个for语句循环体,作用是延时。
向P0口送数0x55,目的是点亮D1、D3、D5、D7四个LED。
序号17~21:
5.4文件包含处理
我们还没有解释过预处理命令中的包含头文件REG51.H(即include<
)。
包含头文件即为文件包含处理。
所谓“文件包含”是指一个文件将另外一个文件的内容全部复制并包含进来,所以这里的预处理命令虽然只有一行,但C编译器在处理的时候却要处理几十乃至几百行。
包含头文件REG51.H的目的是为了要使用P1这个符号,即通知C编译器,程序中所写的P1是指80C51单片机的P1端口而不是其它变量。
用记事本打开C:
/Keil/C51/Inc/Reg51.h(打开时的文件类型改为所有文件)可以看到这样的一些内容:
/*--------------------------------------------------------------------------
REG51.H
Headerfileforgeneric80C51and80C31microcontroller.
Copyright(c)1988-2002KeilElektronikGmbHandKeilSoftware,Inc.
Allrightsreserved.
--------------------------------------------------------------------------*/
#ifndef__REG51_H__
#define__REG51_H__
/*BYTERegister*/
sfrP0=0x80;
sfrP1=0x90;
sfrP2=0xA0;
sfrP3=0xB0;
sfrPSW=0xD0;
sfrACC=0xE0;
sfrB=0xF0;
sfrSP=0x81;
sfrDPL=0x82;
sfrDPH=0x83;
sfrPCON=0x87;
sfrTCON=0x88;
sfrTMOD=0x89;
sfrTL0=0x8A;
sfrTL1=0x8B;
sfrTH0=0x8C;
sfrTH1=0x8D;
sfrIE=0xA8;
sfrIP=0xB8;
sfrSCON=0x98;
sfrSBUF=0x99;
/*BITRegister*/
/*PSW*/
sbitCY=0xD7;
sbitAC=0xD6;
sbitF0=0xD5;
sbitRS1=0xD4;
sbitRS0=0xD3;
sbitOV=0xD2;
sbitP=0xD0;
/*TCON*/
sbitTF1=0x8F;
sbitTR1=0x8E;
sbitTF0=0x8D;
sbitTR0=0x8C;
sbitIE1=0x8B;
sbitIT1=0x8A;
sbitIE0=0x89;
sbitIT0=0x88;
/*IE*/
sbitEA=0xAF;
sbitES=0xAC;
sbitET1=0xAB;
sbitEX1=0xAA;
sbitET0=0xA9;
sbitEX0=0xA8;
/*IP*/
sbitPS=0xBC;
sbitPT1=0xBB;
sbitPX1=0xBA;
sbitPT0=0xB9;
sbitPX0=0xB8;
/*P3*/
sbitRD=0xB7;
sbitWR=0xB6;
sbitT1=0xB5;
sbitT0=0xB4;
sbitINT1=0xB3;
sbitINT0=0xB2;
sbitTXD=0xB1;
sbitRXD=0xB0;
/*SCON*/
sbitSM0=0x9F;
sbitSM1=0x9E;
sbitSM2=0x9D;
sbitREN=0x9C;
sbitTB8=0x9B;
sbitRB8=0x9A;
sbitTI=0x99;
sbitRI=0x98;
#endif
在第4章,我们已经介绍了MCS-51单片机的基本结构,对80C51内部结构应该比较熟悉,因此从中可看出,这里都是一些符号的定义,即规定符号名与地址的对应关系。
其中有“sfr
P1
=
0x90;
”一行,即定义P1口与地址0x90对应(0x90是C语言中十六进制数的写法,相当于汇编语言中的90H)。
5.5通用的C语言程序组成结构
美国国家标准化协会(ANSI)制订的C语言标准(ANSIC)中规定,函数必须要“先声明,后调用”,我们在“5.1用函数调用方式完成LED1~LED8这8个二极管实现D0、D2、D4、D6及D1、D3、D5、D7的交替点亮的实验,周期约1秒”的实验中,是先定义(声明)了几个功能子函数,然后在主函数中进行调用,这样当然没问题。
如果将顺序倒一下,将几个功能子函数放在主函数的后面,那么编译时就会出错。
这时我们需要进行“先声明,后调用”。
下面就是“先声明,后调用”的例子。
//1
//===================================2
voiddelay(void);
//3
voidlight1(void);
//4
voidlight2(void);
//5
//===================================6
voidmain(void)//7
{//8
while
(1)//9
{//10
//11
//12
//13
//14
}//15
}//16
/*===================================17================*/
voiddelay(void)//18
{//19
//20
i++)//21
{//22
j++)//23
}//24
}//25
}//26
//====================================27============
voidlight1(void)//28
{//29
//30
}//31
//====================================32============
voidlight2(void)//33
{//34
//35
}//36
以上的程序中,序号3~5为自定义功能子函数的声明,其它可参考“5.1用函数调用方式完成LED1~LED8这8个二极管实现D0、D2、D4、D6及D1、D3、D5、D7的交替点亮的实验,周期约1秒”的程序解释,由于符合ANSIC规定的“先声明,后调用”原则,因此编译时通过。
所以,一个好的习惯是,不管自定义的功能子函数放在什么位置,在程序的开始处总是先进行声明。
下面是通用的C语言程序组成结构(主函数可放在功能子函数声明之后的任意位置):
功能子函数1声明
功能子函数2声明
功能子函数n声明
功能子函数2ji_light()
功能子函数nou_light()
5.6将程序的若干个函数保存在几个源程序文件中,最后再将它们连接在一起,实现D0、D2、D4、D6及D1、D3、D5、D7这8个二极管的交替点亮的实验,周期约1秒
5.6.1实现方法
第5章的一开始提到过,组成一个程序的若干个函数可以保存在一个源程序文件中,也可以保存在几个源程序文件中,最后再将它们连接在一起。
现在我们具体做一下实验,看看实现的方法。
首先我们需建立2个源程序文件CS5-3a.c及CS5-3b.c。
CS5-3a.c中输入的是main主函数(若有需要也可输入自定义功能子函数),CS5-3b.c中输入delay、light1、light2这3个自定义功能子函数,将它们分别添加到当前项目组中。
为了能在CS5-3a.c文件中使用在CS5-3b.c文件里定义的delay、light1、light2这3个子函数,在程序的开始处(第2、3、4行),我们将这3个子函数声明为外部函数,说明它们已在其它文件中定义。
这种做法可将一个大型、冗长的程序文件化解成若干个较小的文件,不仅可读性强,而且将任务分解开来,可同时分配给多个程序员开发,便于装配,大大提高了大型复杂软件的开发速度与质量。
5.6.2源程序文件
在D盘建立一个文件目录(CS5-3),然后建立CS5-3.uv2的工程项目,选择芯片并确定选项。
单击“文件”菜单,在下拉菜单中选择“新建”,在编辑窗口中输入以下的源程序:
externvoidlight1(void);
//2,声明该函数已在其它文件中定义。
externvoidlight2(void);
//3,声明该函数已在其它文件中定义。
externvoiddelay(void);
//4,声明该函数已在其它文件中定义。
//=====================5
voidmain(void)//6
{//7
while
(1)//8
{//9
//10
//11
//12
//13
}//14
}//15
将上述源程序保存为CS5-3a.c。
再单击“文件”菜单,在下拉菜单中选择“新建”,在编辑窗口中输入以下的源程序:
voiddelay(void)//2
{//3
i++)//5
j++)//7
}//8
}//9
}//10
//====================11============
voidlight1(void)//12
{//13
//14
//====================16============
voidlight2(void)//17
{//18
//19
}//20
将上述源程序保存为CS5-3b.c。
单击工程管理器中“Target1”前的“+”号,出现“SourceGroup1”后再单击,加亮后右击。
在出现的下拉窗口中选择“AddFilestoGroup‘SourceGroup1’”,在增加文件窗口中选择CS5-3a.c文件,鼠标单击“Add”按钮,这时CS5-3a.c文件加入到SourceGroup1这个组;
同理,再选择CS5-3b.c文件,鼠标单击“Add”按钮,这时CS5-3b.c文件也加入到SourceGroup1这个组,见图5-2。
随后关闭此对话窗口。
图5-2添加2个文件到当前项目组
下来进行编译,编译通过后,试验板接通5V稳压电源,将生成的CS5-3.hex文件下载到51MCUDEMO试验板上的单片机89S51中,注意,标示“LED”的双排针应插上8个短路块。
我们看到D0~D7这8个二极管开始闪亮,其效果与“5.1用函数调用方式完成LED1~LED8这8个二极管实现D0、D2、D4、D6及D1、D3、D5、D7的交替点亮的实验,周期约1秒”完全相同。
5.6.3程序分析解释
源程序文件CS5-3a.c的2、3、4行已特别作了注释,其它分析可参考“5.1.2程序分析解释”。
5.7将程序的若干个函数保存在几个源程序文件中,最后用文件包含的方法将它们连接在一起,实现D0、D2、D4、D6及D1、D3、D5、D7这8个二极管的交替点亮的实验,周期约1秒
5.7.1实现方法
我们建立起2个源程序文件CS5-4a.c及CS5-4b.c。
CS5-4a.c中输入的是main主函数(若有需要也可输入自定义功能子函数),CS5-4b.c中输入delay、light1、light2这3个自定义功能子函数。
为了能在CS5-4a.c文件中使用CS5-4b.c文件,在程序开始的第2行,我们使用了文件包含处理#include"
CS5-4b.c"
,将CS5-4b.c包含到CS5-4a.c文件中,在添加文件时只需将CS5-4a.c添加到当