单片机C语言.ppt
第五章函数 5 1函数的定义 函数定义的一般格式如下 函数类型函数名 形式参数表 reentrant interruptm usingn 形式参数说明 局部变量定义函数体 前面部份称为函数的首部 后面称为函数的体 格式说明 1 函数类型函数类型说明了函数返回值的类型 2 函数名函数名是用户为自定义函数取的名字以便调用函数时使用 3 形式参数表形式参数表用于列录在主调函数与被调用函数之间进行数据传递的形式参数 4 reentrant修饰符这个修饰符用于把函数定义为可重入函数 所谓可重入函数就是允许被递归调用的函数 函数的递归调用是指当一个函数正被调用尚未返回时 又直接或间接调用函数本身 一般的函数不能做到这样 只有重入函数才允许递归调用 关于重入函数 注意以下几点 1 用reentrant修饰的重入函数被调用时 实参表内不允许使用bit类型的参数 函数体内也不允许存在任何关于位变量的操作 更不能返回bit类型的值 2 编译时 系统为重入函数在内部或外部存储器中建立一个模拟堆栈区 称为重入栈 重入函数的局部变量及参数被放在重入栈中 使重入函数可以实现递归调用 3 在参数的传递上 实际参数可以传递给间接调用的重入函数 无重入属性的间接调用函数不能包含调用参数 但是可以使用定义的全局变量来进行参数传递 5 interruptm修饰符 interruptm是C51函数中非常重要的一个修饰符 这是因为中断函数必须通过它进行定义 在C51程序设计中 当函数定义时用了interruptm修饰符 系统编译时把对应函数转化为中断函数 自动加上程序头段和尾段 并按MCS 51系统中断的处理方式自动把它安排在程序存储器中的相应位置 在该修饰符中 m的取值为0 4 对应的中断情况如下 0 外部中断01 定时 计数器T02 外部中断13 定时 计数器T14 串行口中断其它值预留 编写MCS 51中断函数注意如下 1 中断函数不能进行参数传递 如果中断函数中包含任何参数声明都将导致编译出错 2 中断函数没有返回值 如果企图定义一个返回值将得不到正确的结果 建议在定义中断函数时将其定义为void类型 以明确说明没有返回值 3 在任何情况下都不能直接调用中断函数 否则会产生编译错误 因为中断函数的返回是由8051单片机在中断函数完成后自动返回的 如果在没有实际中断情况下直接调用中断函数 操作结果会产生一个致命的错误 4 如果在中断函数中调用了其它函数 则被调用函数所使用的寄存器必须与中断函数相同 否则会产生不正确的结果 5 C51编译器对中断函数编译时会自动在程序开始和结束处加上相应的内容 具体如下 在程序开始处对寄存器入栈 结束时出栈 中断函数加usingn修饰符的 是工作寄存器组选择位 C51编程看不到这些寄存器组 不用加usingn修饰符 6 C51编译器根据中断号 也即interrupt后面的数字 产生一个到中断函数入口地址的绝对跳转 7 中断函数最好写在文件的尾部 并且禁止使用extern存储类型说明 防止其它程序调用 6 usingn修饰符修饰符usingn用于指定本函数内部使用的工作寄存器组 其中n的取值为0 3 表示寄存器组号 C51编程看不到这些寄存器组 不用加usingn修饰符 由C51编译器自动选择 5 2函数的调用与声明 1 函数的调用函数调用的一般形式如下 函数名 实参列表 对于有参数的函数调用 若实参列表包含多个实参 则各个实参之间用逗号隔开 按照函数调用在主调函数中出现的位置 函数调用方式有以下三种 1 函数语句 把被调用函数作为主调用函数的一个语句 2 函数表达式 函数被放在一个表达式中 以一个运算对象的方式出现 这时的被调用函数要求带有返回语句 以返回一个明确的数值参加表达式的运算 3 函数参数 被调用函数作为另一个函数的参数 2 自定义函数的声明 在C51中 函数原型一般形式如下 extern 函数类型函数名 形式参数表 函数的声明是把函数的名字 函数类型以及形参的类型 个数和顺序通知编译系统 以便调用函数时系统进行对照检查 函数的声明后面要加分号 如果声明的函数在文件内部 则声明时不用extern 如果声明的函数不在文件内部 而在另一个文件中 声明时须带extern 指明使用的函数在另一个文件中 例5 1函数的调用 include includeintmax intx iny voidmain void inta b SCON ox52 串口初始化TMOD 0 x20 TH1 0 xf3 TR1 1 scanf pleaseinputa b d d 例5 2外部函数的使用程序1串口初始化 include includevoidserial init void SCON ox52 串口初始化TMOD 0 x20 TH1 0 xf3 TR1 1 程序2 include includeexternserial init 外部定义的函数externintmax inta intb 外部定义的函数voidmain viod serial init scanf pleaseinputa b d d 5 3函数的嵌套与递归 1 函数的嵌套在一个函数的调用过程中调用另一个函数 C51编译器通常依靠堆栈来进行参数传递 堆栈设在片内RAM中 而片内RAM的空间有限 因而嵌套的深度比较有限 一般在几层以内 如果层数过多 就会导致堆栈空间不够而出错 例5 3函数的嵌套调用 include includeexternserial init intmax inta intb intz z x y x y return z intadd inta intb intc intd intresult result max a b max c d return result main inty serial init y add 7 4 3 2 printf d y while 1 在主函数中调用add 而在add 中又调用max 形成嵌套调用 2 函数的递归递归调用是嵌套调用的一个特殊情况 如果在调用一个函数过程中又出现了直接或间接调用该函数本身 则称为函数的递归调用 在函数的递归调用中要避免出现无终止的自身调用 应通过条件控制结束递归调用 使得递归的次数有限 函数的嵌套调用在实际工作中常见 但函数的递归调用使用较少 只用在专门的计算中 例如阶乘 例5 4函数的递归调用 求阶乘doublefact intn if n 0 n 1 return 1 elsereturn n fact n 1 main intnum printf nintputnum scanf d 5 4 1局部变量 在一个函数内部定义的变量是局部变量 它只在本函数内部有效 关于局部变量有几点要注意 1 主函数中定义的变量也是局部变量 而不能因为是在主函数中定义的 就在程序中其它地方使用 主函数也不能使用其它函数中定义的变量 2 不同函数可以使用相同名字的变量 它们只在所定义的函数中有效 互不干扰 3 函数的形参也是局部变量 只在本函数中有效 其它函数不能引用 4 在一个函数内部 复合语句中可以定义变量 这种变量只在本复合语句中有效 例5 5局部变量的定义 intf1 inta 形参a也是局部变量 它只在函数f1中有效 intb c 变量b c只在函数f1中有效 intd 变量d只在本复合语句中有效d b c 变量b c在函数f1中定义 本复合语句在函数f1中 变量b c有效 voidmain void intb c 变量b c只在主函数中有效 它于f1中变量b c使用相同名字 互不干扰 5 4 2全局变量 在所有函数外部定义的变量叫外部变量 外部变量也叫全局变量 它的有效范围是定义变量的位置开始到整个程序的结束 关于全局变量有几点要注意 1 在整个程序中 每个函数都可以引用全局变量 通过全局变量各函数间建立了联系 2 如果一个函数中改变了全局变量的值 则在整个程序中 此全局变量的值均改变 3 全局变量的名字和局部变量的名字不能相同 否则会产生干扰 4 使用全局变量可以减少函数实参和形参个数 从而可以节省内存空间 5 由于全局变量在整个程序运行期间都占有内存空间 而且函数的通用性 清晰性有所降低 所以在程序中 应尽量少用 例5 6全局变量的使用 includefloatmax min 定义全局变量voidsum void 定义sum函数voidmain void max 10 在main函数中给全局变量赋值min 0 sum 调用sum 利用全局变量传参数 减少函数实参和形参个数 voidsum void printf max d min d sum d max min max min 在sum函数中使用全局变量 第五章习题1 说明函数定义的一般格式 2 函数定义格式中 函数的类型 函数的形式参数表有什么作用 3 什么函数声明时须带extern 4 什么叫函数的递归调用 它多用在什么地方 5 什么叫函数的嵌套 它使用时有什么限制 6 什么叫局部变量 什么叫全局变量 它们使用时有什么不同 7 中断服务函数和一般函数在定义和函数返回值方面有什么不同