第六章 函数 单片机C

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第六章函数一、函数的分类与定义1、函数的分类从C语言程序的结构上划分,C语言函数分为主函数main()和普通函数两种,而对于普通函数,又可以分为标准库函数和用户自定义函数。1)标准库函数标准库函数是由C编译系统提供的库函数,在C编译系统中将一些独立的功能模块编写成公用函数,并将它们集中存放在系统的函数库中,供程序设计时使用,称之为标准库函数。丰富的可直接调用的库函数是C51功能及其高效率的重要体现之一,多使用库函数使程序代码简单,结构清晰,易于调试和维护。C51几类重要库函数及简要说明对于标准C中原有的,在此不再说明,如MATH.H中的各数学运算函数。①专用寄存器include文件包括了所有8051的SFR及其位定义,一般系统都必须包括本文件。如REG51.H,AT89X51.H等。②绝对地址文件absacc.h,见P337该文件中实际只定义了几个宏,以确定各存储空间的绝对地址,如定义了XBYTE宏,允许用户访问8051外部数据存储器中的某一字节。③存储器分配函数,位于stdlib.h中,见P340表B-11.④字符串操作函数位于“string.h”中,见P341其中包括拷贝比较移动等函数如:memccpy、memchr、memcmp、memcpy、memmove、memset这些函数对缓冲区进行处理很方便。⑤流函数输入输出,位于“stdio.h”中,见P344。流函数缺省为8051串口,如要修改,比如改为LCD显示,可修改lib目录中的getkey.c及putchar.c源文件,然后在库中替换它们即可定义用户的I/O口数据读写。2)用户自定义函数用户自定义函数是用户根据自己的需要而编写的函数。从函数定义的形式上可以将其划分无参数函数、有参数函数和空函数。无参数函数被调用时,既无参数输入,也不返回结果给调用函数,它是为完成某种操作而编写的函数。有参数函数在被调用时,必须提供实际的输入参数,必须说明与实际参数一一对应的形式参数,并在函数结束时返回结果供调用它的函数使用。定义空函数的目的是为了以后程序功能的扩充。2、函数的定义C51对函数的功能进行了扩展,函数定义的完整形式如下:返回数据类型函数名(形式参数列表)[reentrant][interruptn][usingm]{局部变量定义函数体;}(注意对于原型函数的说明,和定义函数相似,但无函数体,也不能说明工作寄存器组的切换usingn和中断说明interrupt函数。)其中:1)函数类型静态函数和外部函数。(1)静态函数(内部函数,默认)静态函数只能在定义它的文件中被调用,而不能在其他文件中的函数所调用。(2)外部函数外部函数可以在定义它的文件和其它文件中被调用。可以在函数定义和调用时使用extern说明是外部函数。但必须注意:在一个文件中,若将主程序放到前面,对后面出现的函数,就必须在文件开始进行说明,说明方式同普通C语言。否则出现警告如下:函数**.C(5):warningC206:'func':missingfunction-prototype所以编程时,要习惯将main()放到最后。(见“外部函数”)例:文件1:#includeAT89X51.Hexternadd(x1,x2);unsignedchardatax1=12,x2=2,y;main(){y=add(x1,x2);}文件2:datacharx3;add(x1,x2){x3=x1+x2;returnx3;}2)函数返回值与数据类型如果返回数据,进行说明;如果不返回,一般用void说明,也可以不说明。函数返回值通过returnx返回,返回值是通过函数名带回的,所以一个函数只能有一个返回值。上例中的x3和y。3)形参与实参形参:在定义函数时,函数名后面括号中的变量称为“形参”,定义时不赋值,由调用函数将值传过来。实参:主调用函数后面括号中的表达式为“实参”,实参必须有确定的值。该值在调用时按对应关系传递给形参。C语言中参数传递是单向的。4)可重入函数什么是可重入函数?可重入函数就是允许被递归调用的函数。函数的递归调用是指当一个函数正被调用尚未返回时,又直接或间接调用函数本身。有两种情况,一种是自身循环调用,另一种是其他函数调用,如程序中正在调用某个函数,而中断中也调用,就可能出现同时调用。一般的函数不能做到这样,只有重入函数才允许递归调用。因为8051内部堆栈空间的限制,为了提高效率,C51没有提供这种堆栈,而是提供一种压缩栈。每个函数有一个给定存储空间,用于存放局部变量。函数中的每个变量都存放在这个空间的固定位置。当递归调用该过程时会导致变量被覆盖,所以通常情况下C51中的函数是不能重入的。可重入函数为此必须使用reentrant函数属性来声明函数是可重入的。与不可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为再入函数生成一个模拟栈,通过这个模拟栈来完成参数传递和存放局部变量。这样每次函数调用时的局部变量都会被单独保存,再入函数一般占用较大的内存空间,运行起来也比较慢,并且不允许传递bit类型的变量,也不能定义局部位变量。可重入函数经常在实时系统中应用,也可在中断函数和非中断函数同时调用同一个函数时使用。5)规定函数使用的寄存器组切换usingm可使用using函数说明属性来规定函数所使用的寄存器组。m是一个0-3的整形参数,分别对应0-3组工作寄存器。这个参数表示使用的寄存器组的编号,这个参数不能使用带运算符的表达式。using属性只能在函数定义中使用,不能在函数原型声明中使用。使用using属性的函数将自动完成以下操作:进入函数前,将当前使用的寄存器组的标号保存在堆栈中。更改PSW的寄存器组选择位,选择设定的寄存器组作为当前的寄存器组。函数退出时,将寄存器组恢复成进入函数前的寄存器组。6)中断函数说明interruptnC51最大支持32个中断,在单片机中n常用0-5。对应中断源见P177。注意:仅能在函数定义时使用interrupt函数属性,不能在函数声明时使用interrupt函数属性。中断函数在运行过程中自动完成以下工作:1)当中断产生时,将特殊功能寄存器ACC、B、DPH、DPL、PSW的值将被保存在堆栈中。2)如果中断函数未使用using属性进行修饰,中断函数中所使用的寄存器的值将保存在堆栈中。3)中断函数运行完成退出时,堆栈中保存的数据将被恢复。4)中断函数退出时,其对应的汇编代码使用RETI指令退出。中断函数应遵循以下规则:(1)中断函数不能进行参数传递。(要传递参数怎么办)(2)中断函数没有返回值。(要返回值怎么办)(3)不能在其它函数中直接调用中断函数(4)如果在中断中调用了其他函数,必须保证这些函数和中断函数使用了相同的寄存器组,并且这些函数应为可重入函数。(5)C51编译器从绝地址8n+3产生一个中断向量,其中n为中断号。该向量包含一个到中断函数入口地址的绝对跳转。(因为外部中断0从0003H开始,然后每个中断占8个字节)例1:定义了一个函数intfunc(x1,x2,x3);//函数声明main(){inta1=1,a2=2,a3=3,a4;a4=func(a1,a2,a3);}intfunc(x1,x2,x3){intdatap2;p2=x1+x2+x3;returnp2;}在单片机c语言中,用下面的方法定义更方便:intfunc(x1,x2,x3){intdatap2;p2=x1+x2+x3;returnp2;}main(){inta1=1,a2=2,a3=3,a4;a4=func(a1,a2,a3);}二、函数与指针函数在编译时,编译器为每个函数分配一个入口地址,这个入口地址就称为函数的指针。函数的指针可以赋给函数指针变量,并能通过函数指针变量调用它所指向的函数。指向函数指针变量的定义格式如下:存储器类型数据类型(*指针变量名)(参量列表)存储器类型是定义将指针变量存储的位置。说明:1)函数指针变量定义时,两侧的()是必须的。2)指向函数的指针变量可以指向任何一个格式相同的函数的入口地址。3)C语言约定,函数名本身就是函数的入口地址。4)当函数指针变量指向函数时,即可用它来调用所指的函数。调用格式为(*指针变量名)(实参表)例1:函数指针的使用intadd(inta,intb){returna+b;}intsub(inta,intb){returna-b;}main(){int(*pFunc)(int,int);//定义函数指针变量,注意要定义数据类型intx,y;pFunc=add;//对函数指针变量赋值x=(*pFunc)(3,4);pFunc=sub;//由于两个函数格式相同,故定义了一个函数指针y=(*pFunc)(5,3);}函数的指针类型为普通指针。(函数指针定义)三、函数的调用函数的调用,必须保证被调用函数是已经存在的函数或者是库函数;如果是库函数,必须用#include将所用函数信息包含到程序中。如#includemath.h。对于无参数和无返回值的函数调用很简单,不再重复。在函数调用中,最关键的是参数的传递与返回值,同普通c语言一样,调用时,可以由实元传递多个参数到函数中,但在返回时,只能返回一个值给函数名。1、数组作为函数的参数(函数1)下面调用函数时,实元传递给哑元的是数组名,将数组传递过去,实际是传递地址。#includeat89x52.hintfunc(x)intx[3];//注意说明方法,定义了一个数组,在花括号外{intp2;p2=x[0]+x[1]+x[2];returnp2;}main(){inta[3]={3,6,9},a2;a2=func(a);//数组a作为函数的参数,传数组地址}将上式的数组,也可以改为指针。(函数2)#includeat89x52.hintfunc(x)int*x;//定义指针,也就是数组的首地址,以下要按指针运算{chari;intp2=0;for(i=0;i3;i++)p2+=*(x+i);//*为间接访问运算符returnp2;}main(){inta[3]={3,6,9},a2;a2=func(a);}2、指针作为函数的参数(函数3)#includeat89x52.hintfunc(x)int*x;//必须还要定义相同类型的指针{intp2;p2=2*(*x);//传过来的内容乘2returnp2;}main(){inta=5,a2;int*p=&a;//定义指针并赋值a2=func(p);}//用指针将a的地址传到函数3、用指针作为返回值(函数4)由于返回值只有一个,所以要返回多个值时,就要用数组或者指针。注意指针函数的概念。#includeat89x52.hint*func()//由于返回值为地址,要定义指针函数{intp2[3]={2,4,6};returnp2;}//将数组的首地址返回main(){unsignedchari;inta;int*a2;//接收的是地址a2=func();//将传递过来的地址送入a2中for(i=0;i3;i++)a+=*(a2+i);//分别取数组的3个数相加}特点:一次可以返回多个值四、函数调用时参数的传递规定为了便于混合编程,在单片机c语言中,对函数调用和返回,参数的传递都有严格的规定,参数的传递途径有:寄存器、存储器和堆栈(重入函数);其返回参数均通过寄存器传递。利用寄存器传递参数的规则如下:参数编号charintLong,float一般指针第1个参数R7R6(高),R7(低)R4(高)-R7(低)R1,R2,R3第2个参数R5R4(高),R5(低)使用固定地址存储类型在R3,地址高位在R2,低位在R1第3个参数R3R2(高),R3(低)例如:(1)func(chara)a在R7中传递到函数。(2)intfunc(inta,intb,char*c)参数a、b、c分别通过R6、R7;R4、R5;R1、R2、R3传递。(3)func(floatg,charh)g在R4、R5、R6、R7中传递,h就不能在R7中

1 / 42
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功