首页 第七章 函数

第七章 函数

举报
开通vip

第七章 函数nullnull第7章 函数本章学习内容本章学习内容 函数定义、函数调用、函数原型、函数的参数传递与返回值  递归函数和函数的递归调用  函数封装,函数复用,函数设计的基本原则,程序的健壮性  变量的作用域与存储类型,全局变量、自动变量、静态变量、寄存器变量数学中的函数数学中的函数自变量因变量函数名程序设计中的函数程序设计中的函数不局限于计算 计算类,如打印阶乘表的程序…… 判断推理类,如排序、查找……问题的提出问题的提出读多少行的程序能让你不头疼? 假如系统提供的函数printf()由10行代码替...

第七章 函数
nullnull第7章 函数本章学习内容本章学习内容 函数定义、函数调用、函数原型、函数的参数传递与返回值  递归函数和函数的递归调用  函数封装,函数复用,函数设计的基本原则,程序的健壮性  变量的作用域与存储类型,全局变量、自动变量、静态变量、寄存器变量数学中的函数数学中的函数自变量因变量函数名程序设计中的函数程序设计中的函数不局限于计算 计算类,如打印阶乘 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 的程序…… 判断推理类,如排序、查找……问题的提出问题的提出读多少行的程序能让你不头疼? 假如系统提供的函数printf()由10行代码替换,那么你编过的程序会成什么样子? 实际上一个printf()有上千行代码 main()中能放多少行代码? 如果所有代码都在main()中,怎么团队合作? 如果代码都在一个文件中,怎么团队合作?7.1分而治之与信息隐藏7.1分而治之与信息隐藏分而治之( Divide and Conquer,Wirth, 1971 ) 函数把较大的任务分解成若干个较小的任务,并提炼出公用任务 信息隐藏(Information Hiding, Parnas, 1972) 设计得当的函数可把具体操作细节对外界隐藏起来,从而使整个程序结构清楚 使用函数时,不用知道函数内部是如何运作的,只按照我们的需要和它的参数形式调用它即可 null函数是C语言中模块化编程的最小单位 可以把每个函数看作一个模块( Module ) 如把编程比做制造一台机器,函数就好比其零部件 可将这些“零部件”单独设计、调试、测试好,用时拿出来装配,再总体调试。 这些“零部件”可以是自己设计制造/别人设计制造/现成的MATCH_ word word文档格式规范word作业纸小票打印word模板word简历模板免费word简历 _1713536406287_0产品7.2 函数(Function)的定义7.2 函数(Function)的定义7.2 函数(Function)的定义若干相关的函数可以合并成一个“模块” 一个C程序由一个或多个源程序文件组成 一个源程序文件由一个或多个函数组成7.2.1函数的分类7.2.1函数的分类函数生来都是平等的,互相独立的,没有高低贵贱和从属之分 main()稍微特殊一点点 C程序的执行从main函数开始 调用其他函数后流程回到main函数 在main函数中结束整个程序运行7.2.1函数的分类7.2.1函数的分类标准库函数 ANSI/ISO C定义的标准库函数 符合标准的C语言编译器必须提供这些函数 函数的行为也要符合ANSI/ISO C的定义 第三方库函数 由其他厂商自行开发的C语言函数库 不在标准范围内,能扩充C语言的功能(图形、网络、数据库等) 自定义函数 自己定义的函数 包装后,也可成为函数库,供别人使用7.2.2函数的定义 (Function Definition)7.2.2函数的定义 (Function Definition)类型 函数名(类型 参数1, 类型 参数2, ……) { 声明语句序列 可执行语句序列 return 表达式; }返回值类型函数名标识符,说明运算规则参数表相当于运算的操作数返回运算的结果函数出口null类型 函数名(类型 参数1, 类型 参数2, ……) { 声明语句序列 可执行语句序列 return 表达式; }参数表里的变量(叫形式参数,Formal Parameter)也是内部变量函数体7.2.2函数的定义 (Function Definition)nullvoid 函数名(void) { 声明语句序列 可执行语句序列 return; }函数无返回值,用void定义返回值类型用void定义参数,表示没有参数return语句后无需任何表达式7.2.2函数的定义 (Function Definition)【例7.1a】 计算整数n的阶乘n! 【例7.1a】 计算整数n的阶乘n! /* 函数功能: 用迭代法计算n! 函数入口参数: 整型变量n表示阶乘的阶数 函数返回值: 返回n!的值 */ long Fact(int n) /* 函数定义 */ { int i; long result = 1; for (i=2; i<=n; i++) { result *= i; } return result; } 返回值类型函数名说明函数的功能返回值作为函数调用表达式的值形参表,函数入口函数内部可以定义只能自己使用的变量,称内部变量null 函数名(表达式1, 表达式2, ……); 实际参数(Actual Argument ) 函数调用(Founction Call)时提供的表达式 有返回值时 放到一个数值表达式中 c = max(a,b); 作为另一个函数调用的参数 c = max(max(a,b),c); printf("%d\n", max(a,b)); 无返回值时 函数调用表达式 display(a,b);7.3向函数传递值和从函数返回值函数的参数传递函数的参数传递实参和形参必须匹配 数目一致,类型一一对应(否则会发生自动类型转换)【例7.1】 7.3.2函数原型(Function Prototype)7.3.2函数原型(Function Prototype)在调用函数前先声明其返回值类型、函数名和参数 函数原型有助于编译器对函数参数类型的匹配检查 末尾有一个分号,声明时不要省略形参和返回值的类型【例7.1】 函数定义与函数声明的区别函数定义与函数声明的区别函数定义 指函数功能的确立 指定函数名、函数类型、形参及类型、函数体等 是完整独立的单位 函数声明 是对函数名、返回值类型、形参类型的说明 不包括函数体 是一条语句,以分号结束,只起一个声明作用7.3.3函数封装与防御性程序设计7.3.3函数封装与防御性程序设计函数封装(Encapsulation)使得外界对函数的影响仅限于入口参数,而函数对外界的影响仅限于一个返回值和数组、指针类型的参数 【例7.1】 Why?传入负数实参 会怎样?防御性程序设计(Defensive Programming) 防御性程序设计(Defensive Programming) 如何使函数具有遇到不正确使用或非法数据输入时避免出错的能力,增强程序的健壮性? 在函数的入口处,检查输入参数的合法性 【例7.2】 计算整数n的阶乘n! 防御性程序设计(Defensive Programming) 如何使函数具有遇到不正确使用或非法数据输入时避免出错的能力,增强程序的健壮性? 在函数的入口处,检查输入参数的合法性 防御性程序设计(Defensive Programming) 【例7.2】计算整数n的阶乘n! 防御性程序设计(Defensive Programming) 主函数如何修改? 增加对函数返回值的检验 防御性程序设计(Defensive Programming) 【例7.3】计算整数n的阶乘n! 防御性程序设计(Defensive Programming) 传入负数的实参时Fact()会返回-1吗? 存在死代码的原因何在?防御性程序设计(Defensive Programming) 【例7.3】计算整数n的阶乘n! 防御性程序设计(Defensive Programming) 如何修改程序去除冗余代码? 如何保证不会传入负数实参? 防御性程序设计(Defensive Programming) 【例7.2】计算整数n的阶乘n! null【例7.4】编写计算组合数的程序函数复用nullvoid assert(int expression) expression为真,什么都不作 否则,提示出错,并终止程序。 如: #include #include main() { int x,y,b,a; scanf("%d%d%d",&x,&y,&a); b=x-y*2; assert(b!=0); printf("a/b=%d\n",a/b); }Assert()查错 使用断言(assert)防止某些参数获得非法值,在程序调试和测试时发现错误.null【例7.2】 计算整数n的阶乘n! null1、删除assert()语句 2、#define NDEBUG //不跟踪调试 #include 因为assert()是一个宏,其定义: #ifdef NDEBUG #define assert(x) ((void)0) #else #define assert(e) ((e)?(void)0:_assert(#e,_FILE_,_LINE_)) #endifAssert()只用于调试程序时,调试结束使其不起作用的方法:7.3.4函数设计的基本原则7.3.4函数设计的基本原则 信息隐藏入口参数有效性检查 敏感操作前的检查 调用成功与否的检查null函数的嵌套调用嵌套调用 在调用一个函数的过程中,又调用另一个函数 C语言规定函数不能嵌套定义,但可以嵌套调用 函数是相互平行的 main() { … … a(); }a 函数 { b(); … return; }b函数 { … … return; }①③④⑤⑥⑦②7.4 递归函数(Recursive Function)7.4 递归函数(Recursive Function)7.4.1递归问题的提出 经典的汉诺塔(Hanoi)问题 理解递归的概念 有人曾计算过,当n=64时,所需移动的次数为18446744073709551615,即1844亿亿次 若按每次耗时1微秒计算,则完成64个圆盘的移动将需要60万年 它来自印度神话.相传上帝创造世界时造了3根金刚石柱子,在第一根柱子上从下往上按大小顺序摞着64片黄金圆盘,上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放到第二根柱子上,并且规定,每次只能移动一个圆盘,在小圆盘上不能放大圆盘.有人预言,这件事情完成时宇宙会在一瞬间闪电式地毁灭.也有人相信婆罗门至今仍在一刻不停地移动着圆盘.汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题 A→B,A→C,B→C, A→B,C→A,C→B,A→BABCn=3汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题ABC A→B,A→C,B→C, A→B,C→A,C→B,A→Bn=3汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题ABC A→B,A→C,B→C, A→B,C→A,C→B,A→Bn=3汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题ABC A→B,A→C,B→C, A→B,C→A,C→B,A→Bn=3汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题ABCn更大些 怎么办? A→B,A→C,B→C, A→B,C→A,C→B,A→Bn=3汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题第1步:将问题简化 假设A杆上只有2个圆盘,即汉诺塔有2层,n=2 将1号圆盘从A移到C 将2号圆盘从A移到B 将1号圆盘从C移到B ABC汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题第2步:对于一个有 n(n>1)个圆盘的汉诺塔,将n个圆盘分为两部分:上面 n-1 个圆盘和最下面的n号圆盘。将“上面n-1个圆盘”看成一个整体 将n-1个圆盘从A移到C 将n号圆盘从A移到B 将n-1个圆盘从C移到BACB汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题设计2个函数: 将n个圆盘借助C从A移到B 将一个圆盘从A移到B ACB 将n-1个圆盘从A移到C 将n号圆盘从A移到B 将n-1个圆盘从C移到B汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题递归方法的基本原理 将复杂问题逐步化简,最终转化为一个最简单的问题 最简单问题的解决就意味着整个问题的解决 汉诺塔(Hanoi)问题汉诺塔(Hanoi)问题7.4.2递归函数7.4.2递归函数 long fact(int n) { if (n < 0) return -1; else if (n == 0 || n == 1) return 1; else return n * fact(n-1); }【例7.6】计算n!= n *(n-1)*(n-2)*……*1 函数直接或间接调用自己,称为递归调用(Recursive Call)7.4.2递归函数7.4.2递归函数 unsigned long fact(unsigned int n) { if (n == 0 || n == 1) return 1; else return n * fact(n-1); } 基线情况(base case)一般情况(general case)无需考虑n<0了【例7.6】计算n!= n *(n-1)*(n-2)*……*1 7.4.2递归函数7.4.2递归函数递归调用应该能够在有限次数内终止递归 递归调用若不加以限制,将无限循环调用 必须在函数内部加控制语句,仅当满足一定条件时,递归终止,称为条件递归 任何一个递归调用程序必须包括两部分 递归循环继续的过程 递归调用结束的过程 if (递归终止条件成立) return 递归公式的初值; else return 递归函数调用返回的结果值; nulln!=n×(n-1)! (n-1)!=(n-1)×(n-2)! (n-2)! .. (n-3)! 5! : 4!=4×3! 3!=3×2! 2!=2×1! 1!=1回推过程递推过程每个递归函数必须至少有一个基线条件 一般情况必须最终能简化为基线条件 递归层数太多易 导致栈空间溢出 后果很严重,程序被异常中止 fact(5)=5*fact(4)= 120 fact(4)= 4*fact(3)= 24 fact(3)= 3*fact(2)= 6 fact(2)= 2*fact(1)=2 fact(1)=1mainfact(5)fact(4)fact(3)fact(2)fact(1)递归与迭代递归与迭代用迭代(即循环)方法编写的阶乘函数 unsigned long Fact(unsigned int n) { unsigned long result = 1; unsigned int i; for (i = 1; i <= n; i++) result *= i; return result; } 递归程序遵循了数学中对阶乘的定义 因此递归方法编写程序具有更清晰、可读性更好的优点 递归与迭代递归与迭代1,1,2,3,5,8,......long Fib(int n) { long f; if (n == 0) f = 0; else if (n == 1) f = 1; else f = Fib(n-1) + Fib(n-2); return f; }【例7.7】计算Fibonacci数列 递归与迭代递归与迭代优点: 从编程角度来看,比较直观、精炼,逻辑清楚 符合人的思维习惯,逼近数学公式的表示 尤其适合非数值计算领域 hanoi塔,骑士游历、八皇后问题(回溯法) 缺点: 增加了函数调用的开销,每次调用都需要进行参数传递、现场保护等 耗费更多的时间和栈空间 应尽量用迭代形式替代递归形式7.5变量的作用域和存储类型7.5变量的作用域和存储类型7.5.1变量的作用域 ( Scope ) 指在源程序中定义变量的位置及其能被读写访问的范围 分为 局部变量(Local Variable) 全局变量(Global Variable )局部变量( Local Variable )局部变量( Local Variable )在语句块内定义的变量 形参也是局部变量 特点 生存期是该语句块,进入语句块时获得内存,仅能由语句块内语句访问,退出语句块时释放内存,不再有效 定义时不会自动初始化,除非程序员指定初值 并列语句块各自定义的同名变量互不干扰 形参和实参可以同名全局变量( Global Variable )全局变量( Global Variable )在所有函数之外定义的变量 生存期是整个程序,从程序运行起占据内存,程序运行过程中可随时访问,程序退出时释放内存 有效范围是从定义变量的位置开始到本程序结束 全局变量( Global Variable )全局变量( Global Variable )【例7.8】打印计算Fibonacci数列每一项时所需的递归调用次数 全局变量使函数间的数据交换更容易,更高效,但建议尽量少用,因为谁都可改写它,所以很难确定是谁改写了它7.5.2变量的存储类型( Storage Class)7.5.2变量的存储类型( Storage Class)指数据在内存中存储的方式 即编译器为变量分配内存的方式,它决定变量的生存期 存储类型 数据类型 变量名; C程序的存储类别 auto型(自动变量) static型(静态变量) extern型(外部变量) register型(寄存器变量)null静态存储区中的变量:与程序“共存亡” 动态存储区中的变量:与程序块“共存亡” 寄存器中的变量: 同动态存储区变量的生存期(Lifetime ) 决定何时“生”,何时“灭”7.5.2变量的存储类型( Storage Class)null auto 数据类型 变量名; auto体现在 进入语句块时自动申请内存,退出时自动释放内存 动态局部变量,缺省的存储类型 静态变量 static 数据类型 变量名; static storage class for local variables (declared inside a block or function) - the lifetime of the entire program 生存期为整个程序运行期间自动变量和静态变量null【例7.11】利用静态变量计算整数n的阶乘n! 自动变量和静态变量静态变量仅初始化一次,变量的值可保存到下次进入函数,使函数具有记忆功能null自动变量和静态变量【例7.11】如果静态变量换成自动变量,结果如何? 静态局部变量和全局变量自动初始化为0值。自动变量不初始化时,值是随机值寄存器变量寄存器变量寄存器 CPU内部容量有限、但速度极快的存储器 register 类型名 变量名; 使用频率比较高的变量声明为register ,可使程序更小、执行速度更快 现代编译器有能力自动把普通变量优化为寄存器变量,并且可以忽略用户的指定 所以一般无需特别声明变量为register null全局变量静态外部变量 (只限本文件使用)外部变量 (非静态外部变量允许其他文件引用)局部变量 自动变量,(离开函数,值就消失)寄存器变量(离开函数,值就消失)定义点之前 使用,需用 extern声明静态局部变量 (离开函数,值仍保留)动态局部变量7.5变量的作用域和存储类型程序版式程序版式缩进(Indent)—保证代码整洁、层次清晰的主要手段 良好风格的程序应严格采用梯形层次对应好各层次int IsPrime(int n) { int k, i; k = sqrt((double)n); for (i=2; i<=k; i++) { if (n % i == 0) return 0; } return 1; }#include main() { int i; for (i=2; i<100; i++) { if (IsPrime(i)) printf("%d\t",i); } }程序版式程序版式现在的许多开发环境、编辑软件都支持自动缩进 根据用户代码的输入,智能判断应该缩进还是反缩进,替用户完成调整缩进的工作 VC中有自动整理格式功能 只要选取需要的代码,按ALT+F8就能自动整理成微软的cpp文件格式命名规则命名规则在Linux/UNIX平台 习惯用function_name 本书采用Windows风格函数名命名 用大写字母开头、大小写混排的单词组合而成 FunctionName 变量名形式 “名词”或者“形容词+名词” 如oldValue与newValue等 函数名形式 “动词”或者“动词+名词”(动宾词组) 如GetMax()等 对函数接口进行注释说明 对函数接口进行注释说明 /* 函数功能: 实现××××功能 函数参数: 参数1,表示×× 参数2,表示×× 函数返回值: ××××× */ 返回值类型 函数名(形参表) { … return 表达式; }本章学习内容本章学习内容 函数定义、函数调用、函数原型、函数的参数传递与返回值  递归函数和函数的递归调用  函数封装,函数复用,函数设计的基本原则,程序的健壮性  变量的作用域与存储类型,全局变量、自动变量、静态变量、寄存器变量【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏猜多个数,10次猜不对就猜下一个数 模块分解过程 开始结束初始化退出处理主功能为程序运行所做的准备工作在退出前要做的事情,如打印结果、资源释放等自底向上自顶向下的模块化程序设计【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏开始结束生成数字猜数字【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏开始结束生成数字猜数字是否继续?NY【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏开始结束猜得对吗?NY提示大小次数<10?输入数字NY处理用户输入,判断是否有输入错误, 是否在合法的数值范围内 【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏【例7.12】用函数完成猜数游戏
本文档为【第七章 函数】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_473785
暂无简介~
格式:ppt
大小:2MB
软件:PowerPoint
页数:0
分类:其他高等教育
上传时间:2013-08-07
浏览量:24