第 11章 Cx51函数在高级语言中,函数和另外两个名词,子程序,,,过程,用来描述同样的事情;在 Cx51中,
使用,函数,这个术语。它们都含有以同样的方法重复地去做某件事的意思。主程序 (main( ))可以根据需要用来调用函数。当函数执行完毕时,
就发出返回 (return)指令,而主程序 main( )用后面的指令来恢复主程序流的执行。同一个函数可以在不同的地方被调用,并且函数可以重复使用。
在构成 C语言程序的若干个函数中,必有一个是主函数 main( )。下面所示为 C语言程序的一般组成结构。
全程变量说明
m ain ( ) /* 主函数 * /
{
局部变量说明执行语句
}
主程序
Fu n ctio n _ 1 ( 形式参数表 ) /* 函数 1 */
形式参数说明
{
局部变量说明执行语句
}

Fu n ctio n _ n ( 形式参数表 ) /* 函数 n */
形式参数说明
{
局部变量说明执行语句
}
函数所有的函数在定义时都是相互独立的,
一个函数中不能再定义其他函数,即函数不能嵌套定义,但可以互相调用。
函数调用的一般规则是:主函数可以调用其他普通函数;普通函数之间也可以互相调用,但普通函数不能调用主函数。
一个 C程序的执行从 main()函数开始,
调用其他函数后返回到主函数 main()中,
最后在主函数 main()中结束整个 C程序的运行。
11.1函数的分类
从 C语言程序的结构上划分,C语言函数分为主函数 main( )和普通函数两种。
而对普通函数,从不同的角度或以不同的形式又可以进行如下分类。
从用户使用的角度划分,函数有两种:一种是标准库函数;一种是用户自定义函数。
1,标准库函数 标准库函数是由 C编译系统的函数库提供的。
2,用户自定义函数 用户自定义函数,顾名思义,
是用户根据自己的需要编写的函数。
11.2函数的定义函数有三种形式:无参数函数、有参数函数和空函数。下面讨论这三种函数的具体定义方法。
1,无参数函数的定义方法 无参数函数的定义形式为,
返回值类型标识符 函数名()
{
函数体语句;

无参数函数一般不带返回值,因此,函数返回值类型识别符可以省略。

#include<stdio.h>
#include<reg51.h>
void timer0_initialize (void);
/* 初始化定时器 0 */
void timer0_initialize (void)
{
EA = 0; /* 关中断 */
timer0_tick = 0;
TR0 = 0; /* 停定时器 0 */
TMOD &= 0xF0; /* 清除定时器模式位 */
TMOD |= 0x01; /* 定时器 0设为模式 1 */
TL0 = (TIMER0_COUNT & 0x00FF);
TH0 = (TIMER0_COUNT >> 8);
PT0 = 0; /* 定时器 0设为低优先级中断 */
ET0 = 1; /* 使能定时器 0中断 */
TR0 = 1; /* 启动定时器 0 */
EA = 1; /*开中断 */
}
main( )

timer0_initialize( );
}
上面程序中,实际定义了两个函数 main( )和
timer0_initialize ( )。它们都是无参数函数。
因此它们的返回值标识符可以省略,默认值是
int类型。
在上面的程序中,函数 timer0_initialize( )放在主函数 main( )之前。这是经典的 C写法。但是标准 C(ANSI C)则要求用另一种格式进行规范化书写。首先,即使是无参数函数,其返回值类型标识符也要注明 "void"关键字。而主函数 main( )则要放在文件的前面,被调用的函数应在开头进行原型声明。
2.有参数函数的定义方法 有参数函数的定义形式为返回值类型说明符 函数名(形式参数列表)
形式参数说明

函数体语句;

例 求两个数的积。
# include<stdio.h>
int gcd( p,q)
int p,q;

return( p*q);
}
main()

int result,a=20,b=14;
printf("a=% d,b=% d",a,b);
result = gcd( a,b);
printf("The gcd of% d and% d is% d\ n",a,b,result);

程序运行结果:
a = 20 b = 14
The gcd of 20 and 14 is 280
在本程序中 int gcd (u,v)就是一个典型的有参数函数。其中 int为函数返回值类型标志符,gcd为函数名,而括号中的 p,q则为函数的输入形式参数。在 gcd函数的结尾处有一个返回语句 return(p)。其中 p为函数的返回变量。
3.空函数的定义方法 空函数的定义形式为返回值类型说明符 函数名()
{ }

float min( )
{ }
11.3函数的参数和参数值
C语言采用函数之间的参数传递方式,
使一个函数能对不同的变量进行功能相同的处理、从而大大提高了函数的通用性与灵活性。
函数之间的参数传递,由函数调用时,
主调用函数的实际参数与被调用函数的形式参数之间进行数据传递来实现。
被调用函数的最后结果由被调用函数的 return语句返回给调用函数。
11.3.1形式参数和实际参数定义函数时,函数名后面括号中的变量名称为,形式参数,,简称形参。
在函数调用时,主调用函数名后面括号中的表达式称为,实际参数,。
在 C语言的函数调用中,实际参数与形式参数之间的数据传递是单向进行的,只能由实际参数传递给形式参数,
而不能由形式参数传递给实际参数。
实际参数与形式参数的类型必须一致,否则会发生类型不匹配的错误被调用。函数的形式参数在函数未被调用之前,并不占用实际内存单元。只有当函数调用发生时,
被调用函数的形式参数才被分配给内存单元,此时内存中调用函数的实际参数和被调用函数的形式参数位于不同的单元中。在调用结束后,形式参数所占有的内存被系统释放,而实际参数所占有的内存单元仍然保留并维持原值。
11.3.2函数的返回值主调用函数 main()在调用有参数函数 gcd( )
时,将实际参数 (a,b)传递给被调用函数的形式参数 u,v。 然后,被调用函数 gcd()使用形式参数
u,v作为输入变量进行运算,所得结果通过返回语句 return u返回给主函数,并在主函数的
result=gcd(a,b)句中通过等号赋值给变量
result,这个 return u中 u变量值就是被调用函数的返回值,简称函数的返回值 。
C语言规定,凡不加返回类型标识符说明的函数,都按整型 (int)来处理。如果函数返回值的类型说明和 return语句中表达式的变量类型不一致,则以函数返回类型标识符为标准进行强制类型转换 。
11.4函数的调用
1,函数调用的一般形式为,
函数名(实际参数表列);
对于有参数型函数,若包含多个实际参数,
则应将各参数之间用逗号分隔开。主调用函数的实际参数数目与被调用函数的形式参数的数目应该相等。实际参数与形式参数按实际顺序一一对应传递数据。
如果调用的是无参数函数,则实际参数表可以省略,但函数名后面必须有一对空括号。
2,函数调用的方式例 out_err_message();
result=2*gcd( p,q);
m=max(a,gcd( p,q) );
上述实例说明了函数调用的三种方式。
第一种调用方式,被调用函数名作为主调用函数中的一个语句,此时并不要求被调用函数返回结果数值,只要求函数完成某种操作。
第二种调用方式,函数结果作为表达式的一个运算对象的身份出现在一个表达式中,这要求被调用函数带 return语句,以便返回一个明确的数值参加表达式的运算。
第三种调用方式,被调用函数作为另一个函数的实际参数。
3.对被调用函数的说明 在一个函数中调用另一个函数必须具有以下条件:
(1) 被调用函数必须是已经存在的函数(库函数或用户自定义函数)。
(2) 如果程序中使用了库函数,或使用了不在同一文件中的另外的自定义函数,则应该在程序的开头处使用# include包含语句,将所用的函数信息包括到程序中来。
(3) 如果程序中使用自定义函数,且该函数与调用它的函数同在一个文件中,则应根据主调用函数与被调用函数在文件中的位置,决定是否对被调用函数作出说明。
如果被调用函数出现在主调用函数之后,则应在主调用函数中,
在对被调用函数调用之前,对被调用函数的返回值类型作出说明。
一般的形式为:
返回值类型说明符 被调用函数的函数名();
4,函数的嵌套在 C语言中,一个函数的内部不能定义另一个函数,也就是说,C语言中函数不能嵌套定义;但 C允许嵌套调用函数。即在调用函数的过程中,允许调用另外一个函数 。
5,函数的递归调用在调用一个函数的过程中,又直接或间接地调用该函数本身。这种情况称为函数的递归调用。
C语言的强大优势之一,就在于它允许函数的递归调用。函数的递归调用通常用于问题的求解,可以把一种解法逐次地用于问题的子集表示的场合。它可用于计算含嵌套括号的表达式,还普遍用于检索和分类 trees和 lists的数据结构。
11.5 中断服务函数与寄存器组定义定义中断服务函数的一般形式为:
函数类型函数名(形式参数表)
〔 interrupt n〕 [ using n]
关键字 interrupt后面的 n是中断号,n的取值范围为 0~ 31,编译器从 8n+3处产生中断向量,具体的中断号 n和中断向量取决于不同的 8051系列单片机芯片。
11.6变量的存储方式
11.6.1局部变量与全局变量
局部变量是在一个函数内部定义的变量,它只在定义它的那个函数范围以内有效 。 在此函数之外局部变量即失去意义,因而也就不能使用这些变量了 。 不同的函数可以使用相同的局部变量名,
由于它们的作用范围不同,不会相互干扰 。 函数的形式参数也属于局部变量 。
全局变量是在函数外部定义的变量,又称为外部变量。全局变量可以为多个函数共同使用,其有效作用范围是从它定义的位置开始到整个程序文件结束。如果全局变量定义在一个程序文件的开始处,则在整个程序文件范围内都可以使用它。
局部变量的优先级比全局变量高。如果在同一个程序文件中,全局变量与局部变量同名,则在局部变量的有效作用范围之内,
全局变量不起作用。
11.6.2变量的存储种类在 C语言中变量有四种存储种类,即自动
(auto),外部 (extern),静态 (static)和寄存器
(register)。 这四种存储种类与全局变量和局部变量之间的关系如下图 。
静 态 变 量全 局 变 量外 部 变 量寄 存 器 变 量静 态 变 量自 动 变 量内 部 变 量变 量
( s t a t i c )
( g l o b a l )
( e x t e r n )
( r e g i s t e r )
( s t a t i c )
( a u t o )
变量的存储种类
11.6.3函数的参数和局部变量的存储器模式
Cx51编译器允许采用三种存储器模式:
SMALL,COMPACT和 LARGE。
一个函数的存储器模式确定了函数的参数和局部变量在内存中的地址空间。处于 SMALL模式下的函数的参数和局部变量位于 8051单片机的内部 RAM中,处于 COMPACT和 LARGE
模式下的函数的参数和局部变量则使用 8051单片机的外部
RAM。
在定义一个函数时可以明确指定该函数的存储器模式,一般形式为:
函数类型函数名(形式参数表) [存储器模式 ]
其中,存储器模式是 Cx51编译器扩展的一个选项。不用该选项时即没有明确指定函数的存贮器模式,这时该函数按编译时的默认存储器模式处理。