单片机接口及
Download
1 / 74

广东机电职业技术学院 智能电子教研室 - PowerPoint PPT Presentation


  • 101 Views
  • Uploaded on

单片机接口及 C 程序设计. 广东机电职业技术学院 智能电子教研室. 项目二 8051 C 语言基本应用. 2.1 8051 数据类型. 1 、数据与数据类型. ( 1 )关键字与标识符. 标识符: 是用来标识源程序中某个对象的名字的,这些对象可以是语句、数据类型、函数、变量、数组等等。 C 语言是大小字敏感的一种高级语言,如果我们要定义一个定时器 1 ,可以写做“ Timer1” ,如果程序中有“ TIMER1” ,那么这两个是完全不同定义的标识符。标识符由字符串,数字和下划线等组成,注意的是第一个字符必须是字母或下划线.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '广东机电职业技术学院 智能电子教研室' - bridie


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

单片机接口及C程序设计

广东机电职业技术学院

智能电子教研室


项目二 8051 C语言基本应用

2.1 8051数据类型

1、数据与数据类型


1)关键字与标识符

标识符:是用来标识源程序中某个对象的名字的,这些对象可以是语句、数据类型、函数、变量、数组等等。

C 语言是大小字敏感的一种高级语言,如果我们要定义一个定时器1,可以写做“Timer1”,如果程序中有“TIMER1”,那么这两个是完全不同定义的标识符。标识符由字符串,数字和下划线等组成,注意的是第一个字符必须是字母或下划线

关键字:则是编程语言保留的特殊标识符,它们具有固定名称和含义,在程序编写中不允许标识符与关键字相同。

在KEIL uVision2 中的关键字除了有ANSI C 标准的32个关键字外

还根据51 单片机的特点扩展了相关的关键字。其实在KEIL uVision2 的文本编辑器中编写C 程序,系统可以把保留字以不同颜色显示,缺省颜色为天蓝色。


2) int 整型

int 整型长度为两个字节,用于存放一个双字节数据。分有符号int 整型数signed int和无符号整型数unsigned int,默认值为signed int类型。

(3) long 长整型

long长整型长度为四个字节,用于存放一个四字节数据。分有符号long长整型signed long和无符号长整型unsigned long,默认值为signed long类型。

(4) float 浮点型

占用四个字节


5)* 指针型

指针型本身就是一个变量,在这个变量中存放的指向另一个数据的地址。这个指针变量要占据一定的内存单元,对不同的处理器长度也不尽相同,在C51 中它的长度一般为1~3个字节。指针变量也具有类型。

(6) bit 位标量

bit 位标量是C51 编译器的一种扩充数据类型,利用它可定义一个位标量,但不能定义位指针,也不能定义位数组。它的值是一个二进制位,不是0就是1,类似一些高级语言中的Boolean 类型中的True 和False。


7) sfr 特殊功能寄存器

sfr 也是一种扩充数据类型,点用一个内存单元,值域为0~255。利用它可以访问51单片机内部的所有特殊功能寄存器。如用sfr P1 = 0x90这一句定P1 为P1 端口在片内的寄存器,在后面的语句中我们用以用P1 = 255(对P1 端口的所有引脚置高电平)之类的语句来操作特殊功能寄存器。

(8)sfr16 16 位特殊功能寄存器

sfr16 占用两个内存单元,值域为0~65535。sfr16 和sfr 一样用于操作特殊功能寄存器,所不同的是它用于操作占两个字节的寄存器,如定时器T0和T1。


9) sbit 可录址位

sbit 同位是C51 中的一种扩充数据类型,利用它可以访问芯片内部的RAM 中的可寻址位或特殊功能寄存器中的可寻址位。

如先前我们定义了

sfr P1 = 0x90; //因P1 端口的寄存器是可位寻址的,所以我们可以定义

sbit P1_1 = P1^1; //P1_1 为P1 中的P1.1 引脚

//同样我们可以用P1.1 的地址去写,如sbit P1_1 = 0x91;

这样我们在以后的程序语句中就可以用P1_1 来对P1.1 引脚进行读写操作了。


2 常量与变量

常量:在程序运行过程中,其值不能改变的量

变量:在程序运行中,其值可以改变的量。

#define CONST 60

void main()

{

int variable, result ;

variable = 20;

result = variable * CONST ;

printf(“result = %d\n”,result);

}


3、Cx51存储器类型与8051存储器间的对应关系

8051单片机存储器结构


Cx51存储类型及其大小和值域

此表值得商榷


变量的存储类型定义举例:

char data char1 ;

bit bdata flags ;

float idata s ;

unsigned char name[3][20] ;


4、存储模式

存储模式决定了变量默认存储类型、参数传递区和存储类型说明变量的存储类型。

定义一个变量的格式如下:

[存储种类] 数据类型 [存储器类型] 变量名表

存储种类有四种:

自动(auto),外部(extern),静态(static)和寄存器(register),缺省类型为自动(auto)。


P63

注:如果省略存储器类型,系统则会按编译模式SMALL,COMPACT或 LARGE所规定的默认存储器类型去指定变量的存储区域。


5、8051特殊功能寄存器(SFR)及其Cx51定义


7、位变量(BIT)及其定义

1、位变量Cx51定义的语法

例如:

bit direction_bit ;

bit lock_pointer;

bit display_invers ;

2、sbit定义特殊位

程序举例。


void main(void) //主函数名

{

//这是第一种注释方式

unsigned int a; //定义变量a 为int 类型

/*

这是第二种注释方式

*/

do{ //do while 组成循环

for (a=0; a<50000; a++); //这是一个循环

P1_0 = 0; //设P1.0 口为低电平,点亮LED

for (a=0; a<50000; a++); //这是一个循环

P1_0 = 1; //设P1.0 口为高电平,熄灭LED

}

while(1);

}


8 cx51
8、 Cx51构造数据类型

字符型(char)、整型(int)和浮点型(float)等数据,都属于基本数据类型。

C语言还提供了一些扩展的数据类型。它们是对基本数据类型的扩展,称之为构造数据类型。些按一定规则构成的数据类型有:数组、结构、指针、共用体和枚举等。


数 组

数组是一组具有固定数目和相同类型成分分量的有序集合。其成分分量的类型为该数组的基本类型.

如整型变量的有序集合称为整型数组,字符型变量的有序集合称为字符型数组。这些整型或字符型变量是各自所属数组的成分分量,称为数组元素。

注:

1、构成一个数组的各元素必须是同一类型的变量,不允许在同一数组中出现不同类型的变量。

2、数组数据是用同一个名字的不同下标访问的,数组的下标放在方括号中,而且下标从0开始


一维数组

1.一维数组的定义方式

类型说明符 数组名[整型表达式]

2.数组的初始化

①在定义数组时对数组的全部元素赋予初值。

例: int idata a[6]={0,l,2,3,4,5} ;

②只对数组的部分元素初始化。

例: int idata a[10]={0,l,2,3,4,5} ;

③在定义数组时,若不对数组的全部元素赋初值,则数组的全部元素被缺省地赋值为0。



二维数组

1.二维数组定义的一般形式

类型说明符数组名[常量表达式][常量表达式];

例 int data char[3][8] ;

二维数组的存取顺序是:

按行存取,先存取第一行元素的第O列,1列,2列,……,直到第一行的最后一列;然后返回到第二行开始,再取第二行的第O列,1列,……,直到第二行的最后一列。如此顺序下去,直到最后一行的最后一列。


2.二维数组的初始化

(1)对数组的全部元素赋初值

①分行给二维数组的全部元素赋初值

②也可以将所有数据写在一个花括号内,按数组的排列顺序对各元素赋初值

(2)对数组中部分元素赋初值


2.2 运算符与表达式

1. 算术运算符及其表达式

+加法运算符,或正值符号;

-减法运算符,或负值符号;

* 乘法运算符;

/除法运算符;

%模(求余)运算符。

算术表达式——用算术运算符和括号将运算对象连接起来的式子称为算术表达式。其中的运算对象包括常量、变量、函数、数组和结构等

优先级——指当运算对象两侧都有运算符时,执行运算的先后次序。

结合性——指当一个运算对象两侧的运算符的优先级别相同的运算顺序。

算术运算符的优先级规定为:先乘除模,后加减,括号最优先。


2、Cx51关系运算符、表达式及优先级

< 小于

>大于

<=小于或等于

>=大于或等于

==测试等于

!= 测试不等于

  • 前4种关系运算符(<、>、<=、>=)优先级相同,后两种也相同;前4种优先级高于后两种。

  • 关系运算符的优先级低于算术运算符。 优先级

  • 关系运算符的优先级高于赋值运算符。 算术运算符

  • 关系运算符的结合性为从左至右 关系运算符

  • 关系运算符:用关系运算符将两个表达式 赋值运算符

  • (可以是算术表达式、关系表达式、逻辑表达式及字符表达式等)连接起来的式子称为关系表达式

  • 关系表达式的结果:由于关系运算符总是二目运算符,故它作用在运算对象上产生的结果为一个逻辑值,即真或假。C语言以1代表真,以0代表假。

(高)

(低)


3、Cx51逻辑运算符、表达式及优先级

&&逻辑“与”(AND)

|| 逻辑“或”(OR)

! 逻辑“非”(NOR)

“&&”和“||”是双目运算符,要求有两个运算对象;而“!”是单目运算符,只要求有一个运算对象。

优先级

(高)

!(非)

算术运算符

关系运算符

&&和||

赋值运算符

Cx51逻辑运算符与算术运算符、关系运算符和赋值运算符之间优先级的次序:“!”(非)优先级最高,算术运算符次之,“&&”和“||”一再再次之,最低为赋值运算符。

  • 逻辑表达式的结合性为自左向右

  • 逻辑表达式:用逻辑运算符关系运算符或逻辑量连接起来的式子称为逻辑表达式。

  • 逻辑表达式的值应该是一个逻辑量真或假

  • 逻辑表达式的值与关系表达式的值相同,以0代表假,以1代表真。

(低)


4、Cx51位操作及其表达式

& 按位与

| 按位或

^按位异或

~ 按位取反

<<位左移

>> 位右移

  • 除了按位取反运算符“~”以外,以上位操作运算符都是两目运算符,即要求运算符两侧各有一个运算对象。

  • 位运算只能是整型或字符型数,不能为实型数据。

  • “&”:参加运算的两个运算对象,若两者相应的位都为1,则该位结果值为1,否则为0。

  • “|”:参加运算的两个对象,若两者相应的位中有一个为1,则该位结果为1。

  • “^”:参加运算的两个对象,若两者相应的位值相同,则结果为0;若两者相应的位值相异,则结果为1。

  • “~”是一个单目运算符,用来对一个二进制数按位进行取反,即0变1,1变0。

  • 位左移、位右移运算符“<<”和”>>”,用来将一个数的各二进制位的全部左移或右移若干位;移位后,空白位补0,而溢出位舍弃。


5、自增减运算符、复合运算符及其表达式

  • 自增减运算的作用是使变量值自动加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。

  • 注意:

  • 自增运算(++)和自减运算 (--)只能用于变量,而不能用于常量表达式。

  • (++)和(--)的结合方向是“自右向左”。

凡是二目运算符,都可以与赋值运算符“=”一起组成复合赋值运算符。Cx51共提供10种复合赋值运算符,即:+=,-+,*=,/=,%=,<<=,>>=,&=,^=,|=

a+=b 相当于 a=a+b

a-=b 相当于 a=a-b

a*=b 相当于 a=a*b

a/=b 相当于 a=a/b

a%=b 相当于 a=a%b

a<<=b 相当于 a=a<<b

a>>=b 相当于 a=a>>b

…… 等等


2.3 流程控制

C语言有3种基本结构:

顺序结构

选择结构

循环结构


1、顺序结构及其流程图

main( )

{

loop1:

P0=shuma[k/10000]; P2=0xf7; for(i=0;i<100;i++);

P0=shuma[k%10000/1000]; P2=0xef; for(i=0;i<100;i++);

P0=shuma[k%1000/100]; P2=0xdf; for(i=0;i<100;i++);

P0=shuma[k%100/10]; P2=0xbf; for(i=0;i<100;i++);

P0=shuma[k%10]; P2=0x7f; for(i=0;i<100;i++);

goto loop1;

}

A

B


2、选择结构及选择语句

在选择结构中,程序首先对一个条件语句进行测试。当条件为真(True)时,执行一个方向上的程序流程;当条件为假(False)时,执行另一个方向上的程序流程。如图所示,P代表一个条件。当P条件成立(为真)时,执行A操作,否则执行B操作;但两者只能选择其一。两个方向上的程序流程最终将汇集到一起,从一个出口中退出。

P为真?

A

B


常见的选择语句有:if、else if语句。

选择结构可以派生出另一种基本结构——多分支结构。在多

分支结构中口分为串行多分支结构和并行多分支结构两种情况

程序示例:

#include<reg51.h>

sbit P37=P3^7;

main( )

{

loop1:

if(P37==1) { P0=0x00; P2=0xff;}

else { P0=0xff; P2=0x00;}

goto loop1;

}


start

N

Y

P3.1=0

Y

N

P3.2=0

P3.2=0

N

Y

Yellow

Green

Red

END

(1)程序流程框图

(2)依照该流程图编写程序。


并行多分支语句:开关语句

switch语句的一般形式如下:

switch(表达式)

{

case常量表这式1:{语句1;}break;

case常量表达式2:{语句2;)break;

i

case常量表达式n:{语句n;}break;

default :{语句n+1;}

}


多分支语句:开关语句

void Lcd12864_Location_XY(uchar x,uchar y)

{

switch(y)

{

case 0: y=0x80;break;

case 1: y=0x90;break;

case 2: y=0x88;break;

case 3: y=0x98;break;

default: y=0x80;

}

x=x&0x07;

Lcd12864_Write_Command(x+y);

}


2、循环语句

在许多实际问题中。需要进行具有规律性的重复操作,如求累加和、数据块的搬移等。而计算机的基本特性之一就是具有重复执行一组语句的能力,即循环能力。利用这种循环能力,程序员只要编写一个包含重复执行语句的简短程序,就能执行所需的成千上万次的重复操作。

几乎所有的实用程序都包含有循环结构。循环结构是结构化程序设计的3种基本结构之一。它和顺序结构、选择结构一起共同作为各种复杂程序的基本构造单元。

作为构成循环结构的循环语句,一般是由循环体及循环终止条件两部分组成的。一组被重复执行的语句称为循环体,能否继续重复执行下去,则取决于循环终止条件。

在C语言中用来实现循环的语句有3种:while语句、do while语句、for语句


进入WHILE循环

N

表达式为真?

Y

循环体内重复

操作语句

退出WHILE循环

执行下面的语句

while语句

while语句的一般形式为:

while(表达式)

{语句/*循环体*/}

在这里,表达式是while循环能否继续的条件,而语句部分是循环体,是执行重复操作的部分。只要表达式为真,就重复执

循环体内的语句;反之,则终止while循环,执行循环之外的下一语句。


进入DO WHILE循环

循环体内语句

Y

表达式为真?

N

循环体外下一个语句

do while语句

do while语句的一般形式为:

do

{语句/*循环体*/}

while(表达式);

do while循环语句的执行过程如下:

首先执行循环体语句,然后执行圆括号中的表达式。如果表达式的结果为真(1),则循环继续,并再一次执行循环语句。只有当表达式的结果为假(0)时,循环才会终止,并以正常方式执行程序后面的语句。


for 循环语句

在c语言中,for循环语句是循环语句中最为灵活也是最为复杂的一种。

它不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定但已经给出循环条件的情况。它既可以包含一个索引计数变量,也可以包含任何一种表达式。除了被重复的循环指令体外,表达式模块由3部分组成:

第一部分是初始化表达式。对c语言而言,任何表达式在开始执行时都应该做一次初始化。

第二部分是对结束循环进行测试。对c语言而言,可以是任何一种测试,一旦测试为假,就会结束循环。

第三部分是尺度增量。


for(表达式l;表达式2;表达式3)

{语句;) /*循环体*/

for循环的语句执行过程如下:

①先对表达式1赋初值,进行初始化。

②判断表达式2是否满足给定的循环条件,若满足循环条件,则执行循环体内语句,然后执行第③步;若不满足循环条件,则结束循环,转到第⑤步。

③若表达式2为真,则在执行指定的循环语句后,求解表达式3。

④回到第②步继续执行。

⑤退出for循环,执行下面一条语句。


程序举例:

for循环的语句执行过程如下:

①先对表达式1赋初值,进行初始化。

②判断表达式2是否满足给定的循环条件,若满足循环条件,则执行循环体内语句,然后执行第③步;若不满足循环条件,则结束循环,转到第⑤步。

③若表达式2为真,则在执行指定的循环语句后,求解表达式3。

④回到第②步继续执行。

⑤退出for循环,执行下面一条语句。

int i,sum ;

sum = 0 ;

for(i=0; i<10;i++)

sum+=i ;


2.3.1 模拟交通灯

1、软件实现

#include<reg51.h>

sbit S2=P3^2;

sbit S3=P3^3;

void main()

{

while(1)

{

if( S2==0)

if(S3==0) P1=0x7f; // Yellow light on

else P1=0xdf; // Red light on

else

if(S3==0) P1=0xbf; //Green light on

else P1=0x7f; // Yellow light on

}

}


2、硬件电路


2.4 数码管显示(数组的应用)

2.4.1 数码管的原理

1、数码管内部结构


LED数码管分类:

按其内部结构可分为共阴型和共阳型;

按其外形尺寸有多种形式,使用较多的是0.5"和0.8";

按显示颜色也有多种形式,主要有红色和绿色;

按亮度强弱可分为超亮、高亮和普亮。

正向压降一般为1.5~2V,额定电流为10mA,最大电流为40mA。


2、LED数码管编码方式


3、静态显示方式及其典型应用电路

LED数码管显示分类:静态显示方式和动态显示方式。

⑴ 静态显示方式:每一位字段码分别从I/O控制口输出,保持不变直至CPU刷新。

特点:编程较简单,但占用I/O口线多,一般适用于显示位数较少的场合。

⑵ 动态显示方式:在某一瞬时显示一位,依次循环扫描,轮流显示,由于人的视觉滞留效应,人们看到的是多位同时稳定显示。

特点:占用I/O端线少,电路较简单,编程较复杂,CPU要定时扫描刷新显示。一般适用于显示位数较多的场合。


4、并行扩展静态显示电路


5、串行扩展静态显示电路


7、动态显示方式及其典型应用电路

动态显示电路

连结形式:

  • ① 显示各位的所有相同字段线连在一起,共8段,由一个8位I/O口控制;

  • ② 每一位的公共端(共阳或共阴COM)由另一个I/O口控制。


1)共阴型8位动态显示电路


2)共阳型3位动态显示电路


2.4.2 利用数组实现段码查表

1、程序

/*****************************

数码管实验:显示一位数。

*****************************/

#include<reg51.h>

unsigned int i,k=7;

unsigned char code shuma[16]={ 0X3F,0X06,0X5B, 0X4F, 0X66 , 0X6D

/* 0 1 2 3 4 5*/

,0X7D,0X07,0X7F,0X6F,0X77,0X7C,0X58,0X5E,0X79,0X71};

/* 6 7 8 9 A B C D E F*/

void main() /*主程序 */

{

P0=shuma[k%16]; P2=0xfe; for(i=0;i<100;i++);

}


2、硬件电路


2.5 延时(函数实现)

2.5.1 C函数基本概念

在高级语言中,函数和另外两个名词“子程序”、“过程”用来描述同样的事情;在Cx51中,使用“函数”这个术语。

它们都含有以同样的方法重复地去做某件事的意思。

C语言程序是由一个个函数构成的。在构成C程序的若干个函数中,必有一个是主函数main( )。


C语言程序的一般组成结构:

main( ) /* 主函数 */

{

局部变量说明

执行语句

}

function_1(形式参数表) /* 函数1 */

{

局部变量说明

执行语句

}

……

一个C程序的执行从main()函数开始,条用其他函数后返回到主函数main()中,最后在主函数中结束整个C程序的运行。


函数的分类

1、标准库函数

标准库函数是由C编译系统的函数库提供的。早在C编译系统设计过程中,系统的设计者事先将一些独立的功能模块编写成公用函数,并将它们集中存放在系统的函数库中,供系统的使用者在设计应用程序时使用。故把这种函数称为库函数或标准库函数。

2、用户自定义函数

用户自定义函数,顾名思义,是用户根据自己的需要编写的函数。

从函数定义的形式上划分可以有3种形式:无参数函数、有参数函数和空函数。


函数的定义

例:

#include<stdio.h>

func()

{

printf(“Sub Function\n”);

}

main()

{

printf(“Main function! /n”);

func();

}

函数的定义的一般形式为:

类型标识符 函数名(形式参数表)形式参数说明{说明部分函数体}



函数的参数和函数值

C语言采用函数之间的参数传递方式,使一个函数能对不同的变量进行功能相同的处理,从而大大提高了函数的通用性与灵活性。

函数之间的参数传递,通过主调用函数的实际参数与被调用函数的形式参数之间进行数据传递来实现。被调用函数的最后结果由被调用函数的return语句返回给主调用函数。


1.形式参数和实际参数

形式参数:在定义函数时,函数名后面括号中的变量名称为“形式参数”,简称形参。

实际参数:在函数调用时,主调用函数名后面括号中的表达式称为“实际参数”,简称实参。

int add(int a, int b)

{

int c;

c=a+b;

return c;

}

void main( )

{

int x=3, y=6 , z;

z=add(x,y);

printf(“z=%d\n”,z);

}


C语言的函数调用中,实际参数与形式参数之间的数据传递是单向进行的,只能由实际参数传递给形式参数,而不能由形式参数传递给实际参数。

实参

形参


2.5.2 子函数的定义与调用

C语言中的子程序, 称作为函数;它不是C语言中的标准函数,是编程者编制的自定义函数。它为主程序服务,执行某一功能

函数调用的一般形式

函数调用的一般形式为

函数名 (实际参数表列);

对于有参数型函数,若包含多个实际参数,则应将各参数之间用逗号分隔开。主调用函数的数目与被调用函数的形式参数的数目应该相等。实际参数与形式参数按实际顺序一一对应传递数据。

如果调用的是无参数函数,则实际参数表可以省略,但函数名后面必须有一对空括号。


函数调用的方式

(1)函数调用语句

即把被调用函数名作为主调用函数中的一个语句。

例 print_message();

此时并不要求被调用函数返回结果数值,只要求函数完成某种操作。

(2)函数结果作为表达式的一个运算对象

此时被调用函数以一个运算对象的身份出现在一个表达式中。这就要求被调用函数带有return语句,以便返回一个明确的数值参加表达式的运算。被调用函数gcd为表达式的一部分。它的返回值乘2再赋给变量result

例: z=2*add(x,y);


(3)函数参数

即被调用函数作为另一个函数的实际参数。例其中,gcd(u,v)是一次函数调用。它的值作为另一个函数调用max()的实际参数之一,最后的m变量值为a和u,v的最大公约数之中最大的一个.

例: m= max( a, gcd(u,v) ;


对被调用函数的说明

在一个函数中调用另一个函数必须具有以下条件:

1)被调用函数必须是已经存在的函数(库函数或用户自定义函数)。

2)如果程序中使用了库函数,或使用了不在同一文件中的另外的自定义函数,则应该在程序的开头处使用#include包含语句,将所用的函数信息包括到程序中来。

3)如果程序中使用自定义函数,且该函数与调用它的函数同在一个文件中,则应根据主调用函数与被调用函数在文件中的位置,决定是否对被调用函数作出说明。

①如果被调用函数出现在主调用函数之后,一般应在主调用函数中,在对被调用函数调用之前,对被调用函数的返回值类型作出说明。一般的形式为

返回值类型说明符 被调用函数的函数名();

②如果被调用函数的定义出现在主调用函数之前,可以不对被调用函数加以说明。因为C编译器在编译主调用函数之前,已经预先知道已定义了被调用函数的类型,并自动加以处理。

③如果在所有函数定义之前,在文件的开头处,在函数的外部已经说明了函数的类型,则在主调用函数中不必对所调用的函数再作返回值类型说明。


函数的嵌套和递归调用

在C语言中,函数的定义都是相互独立的,即在定义函数时,一个函数的内部不能包含另一个函数。尽管C语言中函数不能嵌套定义,但允许嵌套调用函数。也就是说,在调用一个函数的过程中,允许调用另外一个函数。当程序变得越来越复杂的时候,为了使一个函数内的源程序限制在60行之内,以提高可读性,在一个函数内应将嵌套调用的层次限制在4~5层以内。有些C编译器对嵌套的深度有一定限制,但这样的限制并不苛刻。就8051系列单片机而言,对函数嵌套调用层次的限制是由于其片内RAM中缺少大型堆栈空间所致。每次调用都将使8051系统把2字节(返回程序计数器的地址)压人内部堆栈,C编译器通常依靠堆栈来频繁地进行参数传递;但8051版C编译器,由于片内RAM有限所致,只好将参数放在一个人工的外部堆栈中。然而即便是使用片内堆栈,倘若不传递参数,那么5~10层的函数嵌套调用也是不成问题的。所以对于小规模程序而言,即使是忽略嵌套调用的层次和堆栈的深度,通常也是安全的。


函数的递归调用

在调用一个函数的过程中,又直接或间接地调用该函数本身。这种情况称为函数的递归调用。

C语言的强大优势之一,就在于它允许函数的递归调用。函数的递归调用通常用于问题的求解,可以把一种解法逐次地用于问题的子集表示的场合。

程序举例:计算n!


int factorial(int n)

{

int result;

if(n==0) result = 0;

else

result = n*fractorial(n-1);

return result;

}

void main()

{

int j;

for(j=0;j<11;j++)

printf(“%d\n”,factorial(j) );

}


ad