




已阅读5页,还剩34页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章函数 5 1函数概述 主函数main不仅是程序的入口函数 而且与其他函数相比较还有许多使用上的限制 例如 它不能被其他函数调用 不能用inline和static来说明等 ANSI ISOC 还规定主函数main的函数类型必须是int 以保证程序的可移植性 图5 1函数调用关系示意图自定义函数是用户根据程序的需要 将某个功能相对独立的程序定义成一个函数 或将解决某个问题的算法用一个函数来组织 5 1函数调用关系示意图 5 2函数的定义和声明 与变量的使用规则相同 在C 程序中函数定要先定义后调用 5 2 1函数的定义 C 中每一个函数的定义都由4部分组成 即函数名 函数类型 形式参数表和函数体 其定义格式如下 函数体 注意 函数头末尾没有也不能有分号 函数体花括号 后面也没有分号 例 定义一个函数sum 下面就函数定义的几个部分分别说明 函数类型和返回值 若函数类型为void 则表示该函数没有返回值 2 函数名 注意自定义函数名与库函数名或系统命名尽量不要相同 3 形式参数表 在函数定义中 当形参个数为0时 应在圆括号中使用void关键字 表示没有形参 这是一个良好的编程习惯 如intf void 4 形参的作用和设计 函数中的形参 用来指定调用此函数时所需要的参数个数和类型 一个函数的函数体中必须有相关语句对形参进行操作 否则形参定义毫无意义 5 函数体 函数体由一对花括号中的若干语句组成 用于实现这个函数的功能 它仅为程序结构而设定 本身不实现任何操作 注意 C 不允许在一个函数体中再定义函数 5 2 2函数的调用和声明 1 函数的实参和形参要注意形参和实参有如下区别2 函数的调用函数调用的一般格式如下 注 调用函数时要注意 实参与形参的个数应相等 类型应一致 且按顺序对应 一一传递数据 3 函数的声明声明一个函数按下列格式进行 下面几种形式都是对sum函数原型的合法声明 intsum inta intb 允许原型声明时的形参名与定义时不同intsum int int 省略全部形参名intsum inta int 省略部分形参名intsum int intb 省略部分形参名 5 3函数的参数特性 5 3 1全局变量和局部变量C 中每一个变量必须先定义后使用 若变量是在函数头或函数体内定义的 则该变量就是一个局部变量 它只能在函数体内使用 函数体外则不能使用 若变量是在函数外部 如在main主函数前 定义的 则它从定义开始一直到源文件结束都能被后面的所有函数或语句引用 这样的变量称为全局变量 includeusingnamespacestd 函数声明和定义voidf intn 函数原型声明inta 8 定义全局变量intmain cout a endl 输出8f 10 cout a endl 输出10return0 voidf intn 函数定义 a n 将全局变量的值改为n 代码中 变量a是在main函数中定义的 是一个全局变量 因此它能在后面的main函数和f函数中使用 程序运行结果如下 810 例Ex Global 使用全局变量示例 5 3 2函数调用的内部机制 函数调用是在栈内存空间中完成的 栈的工作原理是先入后出 当调用一个函数时 整个调用过程分为三步进行 调用初始化 执行函数调用 调用后处理 从图中可以看出 在调用函数sum时 C 首先进行如下初始化步骤 因sum函数调用需要指定实参 这里是10和20 故先从右向左将实参值20 14h 和10 0Ah 分别入栈 在汇编中使用push指令来进行 把函数返回后执行的第一条指令地址入栈 在汇编中call指令隐含一个这样的操作 此时入栈的地址是004015C1 将控制权转交到被调函数处 通过call和jmp指令来进行 然后执行函数调用 其步骤如下所述 将所有相关寄存器的运行状态入栈 即保护现场 在栈中开辟并预留一定数量的临时内存空间 此时函数的形参和函数体中定义的变量的内存空间的分配等操作一并在此步完成 其中形参的内存空间就是已压入栈中的实参值所在的内存空间 执行函数体中的其他语句 z x y returnz 函数在返回时 这里是在 returnz 执行之后 进行如下函数调用后处理的步骤 将返回值保存在栈中的临时内存空间中 如果无返回值 则此步不执行 将所有入栈的相关寄存器运行状态出栈 使用pop指令 即恢复现场 释放栈空间 根据返回地址 回到主调函数 执行下一条指令 即地址004015C1中的指令addesp 8 5 3 3参数传递方式 函数的参数传递有两种方式 一是按值传递 二是地址传递或引用传递 值传递具有如下特点 1 在值传递方式下 若实参指定的是变量 则在调用初始化时入栈的是实参变量的值而不是实参变量的地址2 即使形参的值在函数中发生了变化 函数调用结束后 实参的值不会受到影响 例Ex S 交换函数两个参数的值 includeusingnamespacestd voidsx floaty 函数原型说明intmain floata 20 b 40 cout a a b b n swap a b 函数调用cout a a b b n return0 voidsx floaty 函数定义 floattemp temp x x y y temp cout x x y y n 程序运行结果如下 a 20 b 40X 40 y 20a 20 b 40注 函数值传递方式的最大好处是保持函数的独立性 该方式下 函数只有通过指定函数类型并在函数体中使用return来返回某一类型的数值 5 3 4函数的默认形参值 在C 中 允许在函数声明或定义时给一个或多个参数指定默认值 这样在调用时 可以不给出实际参数 而按指定的默认值工作voiddelay intloops 1000 函数定义 1000为形参loops的默认值 if 0 loops return for inti 0 i loops i 空循环 起延时作用 这样 当有调用delay 和delay 1000 等效时 程序就会自动将loops当1000的默认值来处理 当然 也可在函数调用时指定相应的实际参数值 例如 delay 2000 形参loops的值为2000 在设置函数的默认形参值时要注意以下5点 1 当函数既有原型声明又有定义时 默认参数只能在原型声明中指定 而不能在函数定义中指定 2 当一个函数需要有多个默认参数时 则在形参分布中 默认参数应严格从右到左逐次定义和指定 中间不能跳开 3 当带有默认参数的函数调用时 系统按从左到右的顺序将实参与形参结合 当实参的数目不足时 系统将按同样的顺序用声明或定义中的默认值来补齐所缺少的参数 4 由于对同一个函数的原型可进行多次声明 因此在函数声明中指定多个默认参数时 可用多条函数原型声明语句来指定 但同一个参数的默认值只能指定一次 5 默认参数值可以是全局变量 全局常量 甚至是一个函数 但不能是局部变量 因为默认参数的函数调用是在编译时确定的 而局部变量的值在编译时无法确定 5 4函数的调用特性 在C 中 函数还有重载 内联调用 嵌套调用及递归调用等特性 相应的函数被称为重载函数 内联函数 嵌套函数和递归函数 5 4 1函数重载函数重载是指C 允许多个同名的函数存在 但同名的各个函数的形参必须有区别 要么形参的个数不同 要么形参的个数相同 但参数类型不同 例Ex OverLoad 编程求两个或三个操作数之和 includeusingnamespacestd intsum intx inty intsum intx inty intz doublesum doublex doubley doublesum doublex doubley doublez intmain cout sum 2 5 endl 结果为7cout sum 2 5 7 endl 结果为14cout sum 1 2 5 0 7 5 endl 结果为13 7return0 intsum intx inty returnx y intsum intx inty intz returnx y z doublesum doublex doubley returnx y doublesum doublex doubley doublez returnx y z 程序运行结果如下 71413 7 5 4 2内联函数 例Ex Inline 用内联函数实现求两个实数的最大值 includeusingnamespacestd inlinefloatfmax floatx floaty returnx y x y intmain floata a fmax 5 10 Acout10 5 10 程序运行结果如下 最大的数为 10要注意使用内联函数的一些限制 1 内联函数中不能有数组定义 也不能有任何静态类型的定义 2 内联函数中不能含有循环 switch和复杂嵌套的if语句 3 内联函数不能是递归函数 总之 内联函数一般是比较小的 经常被调用的 大多可在一行写完的函数 并常用来代替以后要讨论的带参数的宏定义 5 4 3函数嵌套调用 C 允许在函数中再调用其他函数 这种调用称为函数的嵌套调用 例Ex Root 用函数嵌套调用求解一元二次方程的根 include includeusingnamespacestd voidprint doubler1 doubler2 intn 2 输出根 默认时根的个数n为2doublesdelta doublea doubleb doublec 计算b b 4 a c的平方根voidroot doublea doubleb doublec 计算并输出根 intmain doublea 2 0 b 6 0 c 3 0 定义并初始化变量root a b c 调用函数return0 voidprint doubler1 doubler2 intn if n 1 cout 方程无根 else cout 方程有 n 根 n cout t根1 r1 if n 2 cout t根2 r2 cout endl doublesdelta doublea doubleb doublec doubleres doubled b b 4 0 a c 当d小于0无平方根 返回值为 1 若小于1 0e 10 则d认为是0 返回值为0 否则调用sqrt库函数求平方根if d 0 0 res 1 0 elseif fabs d 1 0e 10 res 0 0 elseres sqrt d returnres voidroot doublea doubleb doublec doubled sdelta a b c 调用函数sdelta 此时形参作为函数sdelta的实参if d 0 0 print 0 0 0 0 0 方程无根 输入结果elseif 0 0 a 如果a值为0 则无论d是何值 根都是 c b 如果b值还为0 则无根if 0 0 b print 0 0 0 0 0 elseprint c b 0 0 1 elseif 0 0 d 方程有相同的两个根 即一个根print b 2 0 a 0 0 1 else 方程有两个根print b d 2 0 a b d 2 0 a 程序运行结果如下 5 4 4递归函数 C 允许在调用一个函数的过程中直接或间接地调用函数本身 这称为函数的递归调用 递归 Recursion 是一种常用的程序方法 算法 相应的函数称为递归函数 1 递归函数的实现例如 用递归函数编程求n n n 1 n 2 2 1 它也可用下式表示 1n 0n n n 1 n 0由于n 和 n 1 都是同一个问题的求解 因此可将n 用递归函数longfactorial intn 来描述 程序代码如下 例Ex Factorial 编程求n的阶乘n includeusingnamespacestd longfactorial intn intmain cout factorial 4 endl 结果为24return0 longfactorial intn longresult 0 if 0 n result 1 elseresult n factorial n 1 进行自身调用returnresult 程序运行结果如下 24 2 递归函数的运行过程 intm 0 全局变量longfactorial intn longresult 0 inti for i 0 i m i cout t 按调用的次数来决定输出位置cout hex 程序运行结果如下 可见 尽管函数factorial递归调用时的形参都是n 但每次调用都会为其分配不同的内存空间 且每次调用都是按照调用初始化 执行函数代码 调用后处理这三步进行 因此 递归函数实际上可理解为同名函数的多级嵌套调用 3 递归的条件 一般来说 递归需要满足两个条件 一是要有递归公式 即能将一个问题可化解一个或多个子问题求解 且子问题和原问题具有相同的解法若将 例Ex Factorial 中的函数factorial代码修改成下列代码 longfactorial intn returnn factorial n 1 则因无终止条件而使递归无休止地进行下去 若修改成longfactorial intn longresult 0 if 0 n result 1 elseresult n factorial n returnresult 则递归调用时 由于参数n没有
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 婚姻解除财产分配协议范本:房产、车辆及子女抚养权
- 公共交通空调设备采购、安装及定期检修合同
- 住宅小区消防系统安全性能检测与维保服务协议
- 离婚协议谈话笔录:离婚后子女监护权与抚养费协议
- 班组模具安全培训内容课件
- 成语互动游戏课件
- 胡杨之地 阅读答案
- 2025年麻醉科常用镇痛药物使用技巧考试卷答案及解析
- 中国历史文选 课件 第十七讲 周瑜传;第十八讲 释老志
- 奇妙的生命课件
- 2025年心理辅导员职业资格考试试卷及答案
- 医院法律法规培训内容
- 科技创新管理办法细则
- 飞书使用教程培训
- 肺炎护理考试试题及答案
- 带状疱疹诊疗指南课件
- 肩关节脱位的治疗讲课件
- 极地车辆轻量化复合材料结构-洞察阐释
- 婴幼儿托育专业教学标准(中等职业教育)2025修订
- 劳务外包工安全管理制度
- 2025-2030中国对香豆酸市场发展形势与未来趋势研究报告
评论
0/150
提交评论