第6章06-Cx51函数
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
6.4.2 函数调用的方式
1. 函数用作语句
把被调用函数名称以及传给它的实际参数当作一 条语句,不关心它的返回结果: void main() { : Func(); : }
2. 函数结果作为表达式的一个运算对象
被调用函数代表其返回结果出现在一个运算表 达式中: void main() { : a=12*Func(2)*b; : }
3. 空函数的定义方法
空函数的定义形式为: 返回值类型关键词 函数名(形式参数列表)参数说明 {return 默认值;} 空函数也可以没有返回值,此时其返回类型 关键词为void,函数体则为空:
int fun(int a) { void fun(int a){} return a; } 没有返回值时其返回类型关键词为void,没有 返回类型说明的默认为int,具有int返回值的函数 没有返回语句return时编译器不会提供警告信息。
6.1 函数的分类
从C语言程序的结构上分,C语言函数分为主函 数main()和普通函数两种。对于普通函数,从用户 的角度划分,函数后标准库函数和用户自定义函数 两种: A. 标准库函数: 运行库中提供了很多短小精悍的函数,你可以 很方便地使用它们,你自己很难写出比它们更好的 代码了。
标准库函数如下表所示:
cosh exp memcc sqrt malloc sin ceil acos strrpbrk
B. 用户自定义函数:
用户自定义函数就是用户根据自己任务的需要 而设计的函数。 从函数定义的形式上分,则C语言函数可以分为 无参数函数、有参数函数和空函数三种: A. 无参数函数: 此种函数在调用时,无需参数输入,有时甚至 也不返回任何结果,使用它的目的只是完成某个无 需初始化的任务; B. 有参数函数: 此种函数在调用时必须提供参数输入,完成需 要根据某些条件完成的任务;
3. 函数返回值
函数返回值是通过return语句获得的,一个函 数可以由一个以上的return语句,但是多余一个的 返回语句必须在选择结构中出现。 函数返回值可以是一个表达式: return(x>y?x:y); 表示当x>y时返回x,否则返回y。 没有返回值的函数一定要使用关键词void进行 类型说明,省略返回类型说明的函数默认为int。 再强调一次:int返回值的函数缺少返回语句 return时编译器不会提供警告信息,而其它类型返 回值的函数缺少返回语句return时编译器会提供警 告信息。
6.4.5 函数的递归调用
在调用一个函数的过程中,有直接或者间接 地调用该函数本身,称之为函数的递归调用,可 以进行递归调用的函数称之为再入函数,或者重 入函数。 在PC机使用的C语言中再入函数并不稀奇, 但是在8051单片机使用的C语言中,由于内部 RAM资源的限制,一般情况下的函数都不是在入 函数,也就是说,不能直接或者间接调用函数本 身,则给复杂环境下8051系列的应用带来了很大 的限制。
6.4 函数的调用
6.4.1 函数调用的一般形式 6.4.2 函数调用的方式 6.4.3 对被调用函数的说明 6.4.4 函数的嵌套调用
6.4.5 函数的递归调用 6.4.6 用函数指针变量调用函数
6.4.1 函数调用的一般形式
函数调用的一般形式为: 函数名(实际参数表); 对于有参数型函数,若包含多个实际参数, 这应该将各参数用逗号进行分隔。实际参数的数 目、顺序和形式参数的数目、顺序必须相同,假 如实际参数和对应的形式参数类型不同时,必须 保证编译器能够成功进行类型转换。 调用无参数函数时,实际参数表可以省略, 但是在函数名后必须有一对空括号。
例如: #include "stdio.h" 将标准输入、输出头文件包含到程序中来,而 #include "math.h"
则将函数库中专用数学库的函数包含到程序中来, 这样在程序编译时,系统就会自动将函数库中的有 关函数调入到程序中,编译出完整的运行代码。 3. 如果程序中使用自定义函数,且该函数和调用 它的函数在同一个文件中,并在调用它的函数之 后定义的,则该行数必须在被调数在递归调用时也是不用担心变 量被覆盖的,因为局部变量的每次改变都不会影响 调用它的函数中的局部变量的实际使用值。例如: int result; int fact(int n) { if(n==0) result=1; else result=n*fact(n-1); return result; } 每次调用时内层函数对全局变量result改变后外 层函数会重新对全局变量result进行赋值。假如把变 量result的定义放到函数内,定义成局部变量,那么 就更安全了。但是,编译器仍会提出再入警告。
每次调用函数,8051系统都会把2字节返回 程序地址(PC值)压入内部堆栈,除此之外,C编 译器通常还依靠堆栈来频繁地进行参数传递。对 于8051版的C编译器来说,考虑到片内RAM的具 体情况,许多情况下会把参数放到一个人工的外 部堆栈中,位于pdata区或者xdata区。 即便是内部RAM很少,倘若不进行参数传递, 那么5—10层的函数嵌套调用也是不成问题的。所 以对于小规模的应用程序而言,即使忽略函数嵌套 调用的层次和堆栈的深度,实际应用通常也是安全 的。
6.4.6 用函数指针变量调用函数
可以用指针变量指向变量、字符串和数组, 也可以用指针变量指向一个函数。也就是说,可 以用函数的指针来调用函数。 一个函数在编译时,C编译器会给它分配一 个入口地址,这个入口地址就称之为函数的指针 可以用一个指针变量来指向这个函数,然后通过 这个函数指针来调用这个函数:
gets sprinf tanh py Init_me strncmp strspn modf atan
atan2 atoi sscanf log10 mpool cos tan strpbrk atof
printf sinh log free strncat realloc floor asin
atol scanf calloc strcat srand strncpy strcspn pow
3. 函数结果作为另一个函数的参数
被调用函数代表其返回结果出现在一个函数的 实际参数表中: int Time(unsinged char a,int i); int a,b; unsinged char Func(char c); void main() { : a=12*Time(Func(2),b); : }
int fact(int n) { int result; int (*p)(int n); P=fact; if(n==0) result=1; else result=n*(*p)(n-1); return result; } 注意,函数指针定义和调用中(*p)中的括号是必
须的,函数指针定义中形式参数表(int n)也是必 须的,不然用函数指针调用函数时会出现编译错误, 书上有误。
在某些实时应用中,非再入函数是不可取的, 因为函数调用时可能会被中断程序中断,而在中断 程序中可能再次调用这个函数。所以Cx51允许将 函数定义成再入函数,再入函数可被递归调用和多 重调用而不用担心变量被覆盖,因为每次函数调用 时的局部变量都会被单独保存,因为这些堆栈是模 拟的。再入函数一般都比较大,运行起来也比较慢, 模拟栈不允许传递bit类型的变量,也不能定义局 部位标量。
2. 有参数函数的定义方法
有参数函数的定义形式为: 返回值类型关键词 函数名(形式参数列表)参数说明 {函数体语句;} 有参数函数也可以没有返回值,此时其返回 类型关键词为void:
int fun(int a) { int b=1; b*=a; return b; } void fun(int a) { char b=2; b*=a; }
可以使用关键词reentrant直接把函数定义成 再入函数。例如: int fact(int n) reentrant { int result; if(n==0) result=1; else result=n*fact(n-1); return result; } 注意同时修改启动代码Satrtup.a51中关于再 入函数的内容,这样函数fact就成为再入函数了, 递归调用时也不会出现警告信息了。
int Time(unsinged char a,int i); int a,b; unsinged char Func(char c); void main() { : a=12*Time(Func(2),b); : }
6.4.4 函数的嵌套调用
在C语言中,函数的定义都是互相独立的, 即在定义函数时,一个函数的内部不能包含另一 个函数的定义。尽管C语言中函数不能嵌套定义, 但是允许嵌套调用此前已经定义或者作出原型声 明的函数。通常情况下,一个函数内嵌套调用的 层次应当限制在4—5层以内。 有些C编译器对嵌套的层次深度有一定限制, 但是这样的限制并不苛刻。就8051系列单片机而 言,这种层次深度的限制是由片内RAM的数量限 制所导致的。
因为8051内部堆栈空间的限制,Cx51没有 像大系统那样使用调用堆栈。一般C语言中调用过 程中,会把过程的参数和过程中使用的局部变量 入栈。为了提高效率,Cx51没有提供这种堆栈, 而是提供一种压缩栈,每个过程被给定一个空间, 用于存放局部变量。过程中的每个变量都存放在 这个空间的固定位置,当递归调用这个过程时, 会导致变量被覆盖。
4. 函数原型声明
当一个空函数调用在它后面定义的函数时,被 调用的函数必须在调用函数之前进行函数原型声明, 否则编译器不认识调用的函数名。 #include <stdio.h> void func(void); //函数原型声明 int main() { printf("Call fuction func!\n"); func(); //没有前面的声明不能调用 return 0; } void func() {printf("Fuction func here!\n");}
6.4.3 对调用函数的说明
在一个函数中调用另一个函数必须满足如下条件:
1. 被调用函数必须是已经存在的函数(库函数或 者用户自定义函数); 2. 如果程序中使用了库函数,或者使用了不在同 一个文件中的另外的自定义函数,则应该在程序 的开头处使用#include包含语句,将所用的函数 信息包含到程序中来;
六、Cx51函数
6.1 函数的分类
6.2 函数的定义
6.3 函数的参数和函数值 6.4 函数的调用 6.5 数组、指针作为函数的参数 6.6 Cx51程序的优化
主函数main()可以根据需要来调用函数, 当函数执行完毕时,就发出返回指令return, 而主函数用后续的指令来回复主程序流的执 行。同一个函数可以再不同的地方被调用, 而且可以重复多次调用。 C程序由一个个函数构成,在构成C程序的若 干个函数中,必有一个是主函数。所有函数 在定义时都是相互独立的,一个函数中不能 定义其他函数,即函数不能嵌套定义,但是 可以互相调用。
C. 空函数:
这种函数体内没有任何语句,调用它时除了微 小的延时之外没有任何作用。定义这种函数的目的 不是为了完成某个有目的的任务,而是作为一个扩 充方法,为以后的各种升级执行某些任务而预留的 接口。
6.2 函数的定义
1. 无参数函数的定义方法
无参数函数的定义形式为: 返回值类型关键词 函数名() {函数体语句;} 很多无参数函数都没有返回值,此时其返回 类型关键词为void: int fun() void fun() { { int a,b=1; int a,b=1; a=1; a=1; b*=a; b*=a; return b; } }
6.3 函数的参数和函数值
1. 形式参数和实际参数
形式参数:在定义函数时函数名后括号内的参 数,简称为形参; 实际参数:在函数被调用时函数名后括号内的 参数。 在C语言的函数调用中,实际参数与形式参数 之间的数据传递是单方向进行的,只能由实际参数 传递给形式参数,而且实际参数和形式参数的类型 必须一致。