C语言08-Functions-Adva.ppt

上传人:小飞机 文档编号:6503718 上传时间:2023-11-07 格式:PPT 页数:23 大小:227.99KB
返回 下载 相关 举报
C语言08-Functions-Adva.ppt_第1页
第1页 / 共23页
C语言08-Functions-Adva.ppt_第2页
第2页 / 共23页
C语言08-Functions-Adva.ppt_第3页
第3页 / 共23页
C语言08-Functions-Adva.ppt_第4页
第4页 / 共23页
C语言08-Functions-Adva.ppt_第5页
第5页 / 共23页
点击查看更多>>
资源描述

《C语言08-Functions-Adva.ppt》由会员分享,可在线阅读,更多相关《C语言08-Functions-Adva.ppt(23页珍藏版)》请在三一办公上搜索。

1、函数:高级概念,吴清锋2007年秋,提纲,函数的递归调用数组作为函数参数变量的作用域变量的存储类别和声明修饰符动态存储方式和静态存储方式auto修饰符和register修饰符static修饰符extern修饰符变量的定义与变量的声明存储类别小结内部函数和外部函数,结束,函数的递归调用,递归定义:在调用一个函数的过程中又出现直接或间接地调用函数本身的情况,称为函数的递归调用。如何判断递归?上述定义也是递归的判断标准!P173为何要引进递归?简化程序设计,使程序代码简洁易读!两种形式的递归(模型见P171:图8-9,图8-10)直接递归:f1中调用f1间接递归:f1中调用f2、f2再调用f1递归函

2、数的编写与使用应注意:递归函数一定要有出口示例P172,例8.7,8.8汉诺塔问题递归之所以大家觉得难,主要在于不容易被理解。往往用户无法理解怎么用了“递归”,程序如此简单,结果就给算出来了,这么神奇!这个时候需要认真考虑递归调用的实质!,返回,递归编写程序的一些思考,首先要思考有哪些题目可以用递归来解决:问题的特征:问题的解决依赖于类似问题的解决,只不过后者的复杂程度或规模较原来的问题更小,而且一旦将问题的复杂度或规模简到足够小,问题就可解。初学者至少要掌握以下两种情形:情形一:一些似乎也可用循环解决的问题:例8.7,例8.8,P129:6.10此类问题似乎有个特征,可以“归纳”成数学公式!

3、情形二:一些似乎只能用递归的问题:汉诺塔问题对于第一类问题,编程的要点:首先要获得归纳式,然后要把公式表述出来。一般来说,有一个if语句,其条件,即使用来描述递归跳出条件条件成立时,就是执行值确定的情况;条件不成立时,就是执行公式的另一部分;对于第二类问题,编程的要点:就是要把工作思路搞清楚什么是工作思路呢?类似P175第三段什么是工作步骤呢?类似P176第五段一般来说,有一个if语句,其条件:即使用来描述递归跳出条件条件成立时,就是工作的最后一步;条件不成立时,就是工作的其他步骤;,返回,数组作为函数参数(1),分为三个主题:数组元素作为函数实参数组名作为函数实参多维数组名作为函数参数数组元

4、素作为函数实参定义与使用 数组元素可以作为函数的实参,与用变量作实参一样,是单向传递,即“值传送”方式函数定义时,通过定义形参来接受实参的值示例(P178,例8.7.1)回顾下,函数的三要素如何实现使用数组元素来调用函数的关于数组的基本操作及在实际操作时赋初值的作用“/”的作用,返回,注意:这里只是细化了“数组作为函数中的参数”这一具体问题!始终都要考虑函数的三要素!,数组作为函数参数(2),数组名作为函数的参数定义与使用定义时,形参数组采用:类似数组定义的形式:float average(float array10)可以不指定数组大小:float average(float array,in

5、t n)调用时,实参数组是相应的数组名实参数组与形参数组的元素类型应该一致注意用数组名作为参数时,传递的不是数组的值,而是实参数组的首地址实参数组和形参数组大小可以不一致,甚至可以在定义函数时候省略对形参数组长度的定义。思考,传递数组长度的必要性?!由于传递的是地址,所以形式参数和实际参数共享同一段存储空间。其含义:在子函数中对数组元素值的改变将直接影响到主函数中的值。示例,返回,数组作为函数参数(3),多维数组名作为函数的参数定义与使用定义时,形参数组采用:类似数组定义的形式:float average(float array56)可以省略第一维的大小:float average(float

6、 array6)调用时,实参数组是相应的数组名实参数组与形参数组的元素类型应该一致注意用数组名作为参数时,传递的不是数组的值,而是实参数组的首地址实参数组和形参数组大小可以不一致由于传递的是地址,所以形式参数和实际参数共享同一段存储空间。其含义:在子函数中对数组元素值的改变将直接影响到主函数中的值。示例 P183,例8.14 强调两者用相同的一段空间!,返回,变量作用域(1),变量定义的两种位置(位置决定了内外部变量)在所有函数之外在块内的开头部分在块内的开头部分定义的变量称为内部变量。内部变量只在所在块中有效,故称局部变量。函数体是典型的块,所以在函数声明语句部分定义的变量也是局部变量。形式

7、参数只在函数中有效,也是局部变量。在所有函数之外定义的变量称为外部变量。例子外部变量的有效范围为从定义变量的位置开始到所在文件的结束,可以由其有效范围内的多个函数共用,因而也称为全局变量或全程变量。注意,这体现了全局变量的优点!变量的有效范围称为变量的作用域。,内部、外部变量是从定义的位置来看!而全局、局部变量是从有效性来看!思考,什么叫有效性?,变量作用域(2),使用全局变量的优点(P185)使用全局变量的缺点:全局变量在全部执行过程中都占用存储单元。使用全局变量将降低函数的通用性。使用全局变量过多,将导致程序的清晰性降低。如果在变量的作用域中又定义了同名的变量,则内层的变量将“屏蔽”外层变

8、量,使之暂时失效。例子见P187,返回,动态存储方式和静态存储方式,静态存储方式是指在程序运行期间分配固定的存储空间的方式。动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。内存中供用户使用的存储空间情况(P188)全局变量、加static修饰的局部变量放在静态存储区中;形式参数、未加static修饰的局部变量放在动态存储区中。,返回,auto修饰符和register修饰符,auto修饰符函数中的局部变量,如果不专门声明为static存储类别,都是动态地分配存储空间的。这类局部变量称为自动变量。自动变量的特征(P189)定义自动变量用auto修饰符。auto可以省略。regi

9、ster变量变量的值是放在内存中的,C语言还允许将局部变量放在CPU的寄存器中。这种变量叫做寄存器变量使用寄存器变量的好处:提高执行效率定义寄存器变量使用register修饰符只有局部自动变量和形式参数可以作为寄存器变量一个计算机系统中的寄存器数目是有限的,register修饰符只是一种建议。,返回,static修饰符(1),普通局部变量只在函数的本次调用中有效,其值在离开相应函数就消失了。有时希望函数中的局部变量的值在函数调用结束后不消失,下次函数调用时候可以使用。这时应该指定该局部变量为“静态局部变量”,用关键字static进行声明。静态局部变量属于静态存储类别,在静态存储区内分配存储单元

10、。在使用时,静态局部变量一般是在子函数中定义。,static修饰符(2),对静态局部变量是在编译时赋初值的,即只赋初值一次。在定义局部变量时如果不赋初值,则对于静态局部变量将自动被赋0或空字符而自动变量则取一个不确定的值。值得注意的是:静态局部变量在其它函数中是不可见的。P191若非必要,不要多用静态局部变量,需要用局部变量的情况:需要保留函数上一次调用结束时的值如果初始化后,变量只被引用而不改变其值示例,返回,extern修饰符,外部变量的作用域为从变量的定义处开始,到本程序文件的末尾.注意:”外部”的说法来源于其定义的位置!用extern声明变量,可扩展相应外部变量作用域多文件:如果想使用

11、外部程序文件中定义的外部变量(左)文件内:如果想在外部变量定义点之前使用该外部变量(右)用extern声明变量时,类型名可以省略。注意位置!,返回,int x;,extern x;x=3;,extern x;x=3;int x;,变量的定义与变量的声明,除了变量名外变量还有如下两个要素值:变量的内容地址:变量所对应存储单元的首地址对于强类型语言,变量都有类型。类型实际上指出了变量所对应存储单元的大小。变量的定义除了指定变量名和类型的之间的对应关系外,还建立了相应的存储单元。变量的声明则只指定了变量名和类型之间的对应关系。带上extern修饰符实际上是只声明了相应的变量外部变量只能定义一次,而声

12、明可以有多次。变量一定要定义,但未必需要声明。,返回,内部函数和外部函数,内部函数如果一个函数只能被本文件中的其他函数调用,称为内部函数。定义内部函数时在函数首部的最前面应加static。内部函数(静态函数)的功能外部函数在定义函数时,如果在函数首部的最左加上关键字extern表示此函数是外部函数可供其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数.在需要调用外部函数的地方,用extern声明所用的函数是外部函数。注意:内部函数和外部函数强调的是函数在不同文件中!,返回,函数的小结,学习时:一定要慢慢去体会,引进函数的功能牢记函数三个要素关于函数的调用(一定要记住的

13、原则是?)简单调用嵌套调用递归调用(有一个”推”的过程和一个”归”的过程)关于函数的参数变量为实参数组元素为实参数组名为实参(一定要记住”共用存储空间”这一特性!)再从两个角度对变量进行研究(这些角度对程序值的影响!)从变量的作用域角度:全局和局部变量(内部与外部变量)从变量的生存期角度:动态存储和静态存储auto;static;register;extern;内外部函数,返回,示例:数组作为函数参数(1),void swap(int a)int t;t=a0;a0=a1;a1=t;main()int temp2,a,b;scanf(%d%d,注意:引进此例题的目的是要考察数组a和数组temp

14、指向同一组空间.正是由于指向了同一段存储空间,因此在子函数中对值的改变也将体现在主函数中!,示例:数组作为函数参数(2),void sort(int array,int n)int i,j,k,t;for(i=0;in-1;i+)k=i;for(j=i+1;jn;j+)if(arrayjarrayk)k=j;t=arrayk;arrayk=arrayi;arrayi=t;main()int a10,i;printf(enter the arrayn);for(i=0;i10;i+)scanf(%d,返回,一,此题使用选择法排序,该法与冒泡法的异同点:1 冒泡法,在处理的时候,是两个两两交换位置

15、,换一个数,全部数的位置改变;2 选择法,在处理的时候,是只有两个数交换位置,换一个数,只有两个数位置改变。但是,无论如何都必须使用循环的嵌套,并且循环变量间有对应的关系!二,此题的引入同7.2比较,算法没有变化,只是引进了数组作为参数的情况!,注意:1 sort()函数的类型为void2 并且sort()中没有输出语句这是否可以给我们一个借鉴?通过子函数对主函数中相同空间的操作,在主函数中输出,示例:递归函数执行情况分析,#include int i=1;int age(int n)int c;if(n=1)c=10;printf(第%d次返回:值为%d.n,i,c);else c=age(

16、n-1)+2;i+;printf(第%d次返回:值为%d.n,i,c);return c;void main()printf(%d,age(5);,返回,注意:1 找出递归;2 需要研究内在的执行过程,见图8-12,示例:递归函数执行情况分析,#include int i=1;int age(int n)int c;if(n=1)c=10;printf(第%d次返回:值为%d.n,i,c);else c=age(n-1)+2;i+;printf(第%d次返回:值为%d.n,i,c);return c;void main()printf(%d,age(5);,返回,注意:1 找出递归;2 需要研

17、究内在的执行过程,见图8-12原则:发生函数调用时,主调函数的执行在函数调用处暂时被挂起,程序控制离开主调函数转入被调函数执行只有当被调函数执行完后,才返回到主调函数的调用点继续向下执行,所以主调函数一定要在被调函数执行完后才能继续执行并结束。变量的问题:每次调用就新生成新的变量空间(形参和局部变量)!,示例:Fibonacci数列,求Fibonacci数列的前20项int f(int n)if(n=1|n=2)return 1;else return f(n-1)+f(n-2);main()int i;for(i=1;i=20;i+)if(i-1)%5=0)printf(n);printf(%12d,f(i);,返回,注意:1 大家把三种解法都罗列出来,比较下它们的差异!2 程序的不足之处:int!,示例:静态局部变量,#include int x=10;main()int i;for(i=0;i2;i+)printf(%3d,func(x);int func(int y)static int a=5;int x=0;+a;x+;return(a+x+y);,返回,函数func被调用2次全局变量x在函数func中被屏蔽了第一次调用func a初值为5 x初值为0 y的值为10 返回值为17第二次调用func a初值为6 x初值为0 y的值为10 返回值为18,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号