结构化程序设计的基本思想是围绕系统功能自顶向下.ppt
《结构化程序设计的基本思想是围绕系统功能自顶向下.ppt》由会员分享,可在线阅读,更多相关《结构化程序设计的基本思想是围绕系统功能自顶向下.ppt(101页珍藏版)》请在三一办公上搜索。
1、第四章 函数,结构化程序设计的基本思想是:围绕系统功能自顶向下,逐步细化和精化。,主要内容:,函数的概念;,递归的概念和递归函数;,函数的声明(定义)和调用;,细化过程:对系统功能逐层分解出许多易于理解和实现的、逻辑上相对独立的子功能,形成一棵功能树。,在程序实现时,功能树上的每一个结点,对应一个函数。,重点:,函数的声明和函数调用;,递归概念及其应用;,数据的存储类别和作用域。,课堂时数:1012学时,上机时数:45学时,课外上机时数:56学时,数据的存储类别和作用域。,内联函数和重载函数;,4.1 函数的基本概念,我们从结构化程序设计的角度来讨论大程序的功能分解及其函数实现,进而给出函数的
2、定义并讨论函数的作用。,1什么叫函数,把实现特定功能的相关数据和语句组织在一起,并给出相应的名称、类型和参数集,成为程序的一个可调用部分和可共享部分,这种组合形式称为函数。,1)分析,我们将新建、打开、关闭、打印等文件处理功能描述成四个相对独立的函数,分别是:,例4-1 文件处理包括以下几个功能:新建、打开、关闭、打印,请给出有关文件处理的程序框架。,new()、open()、close()、print(),这些函数都包含了各自的数据和语句,并且都有各自的外部名称函数名。,/程序头部void main();new();open();close();print(),2)程序框架,3)调用结构,m
3、ain函数分别调用了new、open、close、print四个函数。,注意:一个C+程序可以包含多个函数,这些函数在定义层次上必须是相同的,但调用层次却可以不相同。,2函数的作用,函数是特定子功能的实现,也即将大程序分割成若干个小程序(子程序)。每个子程序就是一个函数,这样既便于理解也便于实现。,当函数所实现的是程序中频繁使用的一些公共操作,那么该函数就可以重复调用,也就是所谓的“代码重用”。,若干功能相关的函数可以组成一个模块。,综上所述,合理地使用函数便于功能分解和程序实现;可以减少程序的重复编写;可以使程序结构清晰、增强易读性。这就是函数的三大作用。,3函数的分类,通常分为:标准函数、
4、用户自定义函数、类的成员函数三大类。,标准函数也称为库函数,是系统定义的函数,c+提供了种类繁多的库函数,具体可以分为:,I/O类,处理I/O操作;,文件类,处理二进制文件和缓冲型文件;,字符串类,实现字符串运算和字符串操作;,内存类,处理内存分配、释放、测试,传送等工作;,系统类,实现系统调用、系统设置以及底层硬件操作等。,4.2 函数定义,程序中使用函数就象使用数据对象一样,必须先定义后使用。定义函数也称为声明函数。,1作用,给出函数的名称、类型、参数,描述函数内部的数据和语句。,2语法,C+的函数由函数头部(函数原型)和函数体两部分组成,其语法描述为:,()数据说明部分;,/函数头部,解
5、释:,类型标识符:用来说明函数的数据类型,也即函数调用后所返回的调用结果的数据类型,可以是int、char、float、double、void等。如果省略类型标识符的话,则缺省类型是int。如果该函数的类型无关紧要,一般表示成void。,例如:void main(),double max(double x,double y),main的类型是void而max的类型是double。,函数名:C+用标识符作为函数名。函数名具有变量特征,它用来存放返回值。,例如:sqrt(16)sqrt=4;upper(a)upper=A;,参数表:用来说明函数的参数,函数的参数是函数所提供的数据接口,用来与调用者
6、传递数据。因此参数说明必须给出参数的名称、类型。允许一个函数包含0到多个参数,参数之间用逗号分隔。,数据说明部分:位于函数体内,用来说明函数内部所用到的数据对象(变量等),如果函数没有内部数据,则此部分可以省略。,函数说明所定义的参数称为“形式参数”,而函数调用所提供的参数称为“实在参数”。,例如:char upper(char c)只有一个参数,名称为c,类型是字符型。double max(double x,double y)有两个参数x和y,类型都是双精度型。,执行部分,位于函数体内,由若干可执行语句组成,该部分不能省略。,例4-2 编写一个将小写英文字母转换成大写英文字母的函数。,cha
7、r upper(char c)char c1;if(c=a,函数名:upper,类型:字符型;,形式参数:char c;,内部数据说明:char c1;,执行部分:if语句和return函数调用。,例4-3 编写计算字符串长度的函数strlen,int strlen(char s)int i=0;while(si!=0)i+;return(i);,函数名:strlen,类型:整型;,形式参数:char s;s是数组,内部数据说明:int i;,4.3 函数原型,函数原型是一条特殊的说明语句,用来说明程序中所引用的函数的名称、函数返回值类型和参数类型。,1什么叫函数原型,函数原型格式上与函数定义
8、的头部大致相同,但因为是独立的语句,必须以分号结束,并且一般放在程序的顶部。,例4-4 在引用strlen函数的程序中加上其原型说明。,#include int strlen(char*);main()strlen();int strlen(char s),strlen函数的原型说明,2函数原型的语法,函数原型的格式与函数定义的头部格式大同小异,其语法如下:,();,注意:,(1)参数类型表给出函数所有参数的数据类型(无须给出参数名);,(2)函数原型和函数定义在函数类型(返回值类型)、函数名以及参数表这三部分上必须一致,否则就会发生编译错误;,(3)标准库函数的原型都在对应的头文件中(*.h
9、)。如果程序中使用到某些标准库函数,必须在程序顶部,使用#include命令将对应的文件包含进来。,3函数原型的作用,在程序中加入函数原型,有利于保证函数定义和调用上的一致性。这里的一致性是指定义和调用时,函数类型、参数个数和类型方面的一一对应。,一致性的检测工作是由编译系统自动进行的,当程序中加入函数原型,一旦函数原型不正确,也即与函数定义不同,编译系统会报告出错,并给出相应的出错信息。,例如在例4-4的main函数中按以下格式调用strlen函数:strlen(s1,n);,编译程序在编译过程会检测到在调用strlen函数时提供的参数过多的错误,并提示:function does not
10、take 2 parameters(函数在调用中遇到过多的函数参数),void func1(int,float);void main()int a;float b;cin a b;func1(a,b);void func1(int,int)float c;c=a+b;,例4-5 函数原型和函数定义其参数类型不一致的问题示例。,分析:,(1)程序中,func1函数原型和函数调用是一致的,能正确地通过编译。,注意:当所调用的函数是标准函数或该函数不在本程序中,定义和调用的一致性问题就更加重要了。,函数的功能是通过调用实现的,调用时必须提供与函数定义相对应的各个参数的实际值,调用的返回值存放在函数名
11、中,因此函数调用结果可以视为一个数据对象。,4.4 函数调用,将函数调用视为数据对象并作为表达式的成员参加运算,这是函数调用的一般形式。,1函数调用的格式(语法),();,解释:,(1)函数是“按名调用”的,所以函数名必须准确无误,尤其是在调用标准库函数时,一定要保证函数名一字无误。,例如:len=strlen(s);sin(x)/con(x);,例如:要调用scanf()函数,如果将函数名写成scan,便会出现“不能确定的外部函数”这样的错误。此类错误是初学者最容易犯的。,(2)参数表:给出与函数定义所描述的形式参数相对应的各个实在参数。所谓相对应是指:类型一致,个数相等,顺序吻合。,/st
12、rlen:函数名,s:参数,(3)函数调用时所提供的实参其形式可以是常量、变量、表达式、函数调用等。但其值必须是确定的。请看以下一些函数调用:,2函数调用的形式,sqrt(30)sqrt(a)sqrt(a*100)sqrt(abs(x),前面已提到函数调用的一般形式是将其视为数据对象,在参加运算时实现调用。这种数据对象可以出现在以下三种场合,分别对应三种不同的调用形式:,实参是常量;,实参是变量;,实参是表达式;,实参是另一函数调用。,1)表达式,函数调用出现在表达式中,参加所在表达式的运算。,例如:2*sqrt(56),2)函数调用语句,函数调用语句实际上是仅含单个“函数调用数据对象”的特殊
13、表达式,因此是1)的特例。,例如:scanf(),printf()等。,3)函数参数,函数调用所提供的实参是某一函数调用,这种情况称为函数参数。,3函数调用机制和调用过程,例如:max(a,max(b,c)sqrt(abs(x)。,设程序p1具有三个函数,main(),f1(),f2(),其调用关系是main函数调用f1函数,f1函数调用f2函数,程序如下:,void main()int a,b;f1(a);,/main调用f1,p1程序的执行过程如下图所示:,void f1(int x1)char c f2(c);void f2(char x2)float d,/f1调用f2,分析程序的执行
14、过程,可以发现以下特征:,(1)main最先执行、最后结束,f2最后执行却最先结束,正所谓“先进后出”;,main,f1,f2,由f2返回f1,由f1返回main,(2)每一函数在调用另一函数时必须暂时中断自身的执行;,(3)每一个被调用的函数执行结束后,必须能返回主调函数调用处的下一执行点,也即调用前必须保留返回地址。,能够满足以上要求的函数调用机制可以概括为:中断+运行栈,也即c+借助于中断机制和栈来实现函数调用。,1)中断,中断是指主调函数暂时中止自身的运行,转入被调函数的执行部分,待被调用函数运行结束,主调函数又开始余下部分的执行。,2)运行栈,栈是一种“先进后出”的数据结构,也即先压
15、进去的数据最后弹出。C+在处理函数调用时,所使用的栈称为运行栈。,运行栈用来存放返回地址、函数参数以及函数内部定义的数据等。运行栈随着程序的运行而动态变化。,程序p1当运行至的f2被调用时的运行栈如下图所示。,3)调用过程,(1)在运行栈的栈顶建立被调函数的栈空间;,每一个函数的调用过程都包括:调用初始化,执行,善后以及返回三个阶段,具体分为以下步骤:,(2)保护主调函数的运行状态和返回地址(保存在栈区内);,(3)传递参数;,(4)将运行控制权交给被调函数;,(5)被调函数的执行;,(6)恢复主调函数的运行状态;,(7)取出返回地址;,(8)返回主调函数。,执行,4)参数传递,可以看出(1)
16、(4)属于初始化阶段、(5)是执行阶段、(6)(8)是善后阶段。,所谓参数传递就是将实参存入函数栈空间的参数区,用来代替函数定义所描述的形式参数。,(1)参数的传递顺序,按参数表中参数的排列顺序自左向右逐一传递。,例如:函数调用f1(a,b,c),其参数的传递顺序是:先传递a再传递b最后传递c。,(2)参数的传递方式,将实参的值存入对应形参的参数区中。实参可以是变量、常量、表达式。,a.传值,有三种传递方式:,注意:此种方式,被调函数对参数的修改不会影响主调函数用做实参的数据对象。,换句话说,参数的此种传递方式,只能实现数据的单向传递,也即无法由实参带回函数对参数的任何修改。,b.传地址,所谓
17、传地址是将实参的地址存入对应形参的参数区中。实参可以是变量、数组。,传地址与传值的不同之处在于:传递的不是实参的值而是实参这一数据对象的内存地址。,简而言之,这样的参数传递方式,可以实现数据的双向传递,也即可以由实参带回函数对参数的任何修改。,引入这种传递方式目得在于“使得被调函数对参数的修改能够影响主调函数用做实参的数据对象”,并且可以减少数据的传送量。,例:以借书为例,将书直接借给你传值;告诉你借书的地方传地址。,例4-6 试问在下面程序段中,调用函数f1后,a,b,c的值分别是什么?,int a=1,b,c;c=f1(a,分析:,(1)参数a采用传值方式,参数b采用传地址方式。&b表示取
18、b的地址。,(2)调用函数参数f1后,a,b,c的值分别是:a=1,b=10,c=0。为什么?,注意:f1虽然修改了a却无法返回给主调函数,因为a是值参。,c.传名,5)实例,下面我们给出两个函数应用的实例,来进一步阐明函数的定义和调用过程。,例4-7 编写一个程序。分别求两个字符串s1和s2的长度。,(1)分析,我们可以在例4-3所编写的strlen函数的基础上增加一个主函数,该函数两次调用strlen函数。,(2)程序,函数的参数是函数指针,对应的参数必须是函数名。,#include int strlen(char*);void main()int len;len=strlen(abcde
19、fg);cout s1的长度是:len endl;len=strlen(aaa123456);cout s2的长度是:len endl;int strlen(char s)int i=0;while(si!=0)i+;return(i);,/第2次调用strlen函数,/第1次调用strlen函数,1)分析:,我们可以在例4-2所编写的upper函数的基础上增加一个主函数,该函数循环调用upper函数。,例4-8 编写一个程序,用来将输入的字符串s1中的小写字母转换成大写字母。,2)程序:,#include int strlen(char*);char upper(char);void mai
20、n()char s30;int n.i;cout s;,n=strlen(s);for(i=0;i=a;,/循环调用upper函数,/调用strlen函数,计算串长,4函数的嵌套调用,虽然C+不允许在函数内部定义新的函数(嵌套定义),但却允许函数内部调用另一函数,也即允许函数之间相互调用,这种调用形式称为嵌套调用。,/strlen int strlen(char s)int i=0;while(si!=0)i+;return(i);,通过嵌套调用动态地建立函数之间的关系,使得程序的层次化的功能结构得以实现。,1)概念,所谓函数的嵌套调用是指:在函数的执行部分包含对一个或一些函数的调用。,嵌套可
21、以是单重的也可以是多重的,如果被调函数的执行部分不再出现对另一函数的调用,就是单重嵌套。相反,如果被调函数的执行部分也包含对另一函数的调用,就构成多重嵌套。,注意:将函数调用作为实参,不能视为嵌套调用。,例如sin(abs(x),不是sin函数调用abs函数,因为abs函数的调用并没有出现在sin函数的执行部分中。相反是在参数传递的时候就实现的。,2)实例,例4-8所给的程序是函数的单重嵌套调用,并且是并列单重调用。下面分析其调用关系和调用过程。,调用关系:main函数调用strlen函数和upper函数,注意strlen和upper是并列调用。,调用过程:,main,strlen,upper
22、,void main()int a,b f1(a);void f1(int x1)char c f2(c);void f2(char x2)float d,下面的程序包含着函数的多重嵌套调用,main函数调用f1函数,而f1函数又调用f2函数。,4.5 递归函数,在客观世界中,很多问题的处理方法是递归的,例如“折半查找”问题,其处理方法描述为:,(1)取n个有序数据正中间的一个数据dm与查找关键字Key比较,如果dmkey,则可以确定接下去的查找范围是m+1n之间;如果dm=key,则查到所须对象,查找结束。,该方法告诉我们,在查找范围不断变化的情况下,后一步可以使用与前一步相同的方法来进行查
23、找。这种现象是递归调用现象后一步骤调用前一步骤所使用的方法。,(2)用新的查找范围替代上一次的查找范围,重复(1)。,再比如阶乘问题,n!=n*(n-1)*(n-2)*1,这种递推问题也可以用递归调用来实现,即:n!=n*(n-1)!,例4-9 计算阶乘 n!,#include long fact(int);void main();long fact(int n)if(n=1)return(1);else return(fact(n-1)*n);,/在fact函数中调用fact函数递归调用,void main()long result;int n;cout n;if(n=1)result=fa
24、ct(n);cout endl n!=result endl;else cout endl 输入的数据不正确!endl;,fact函数的执行部分,出现自身的直接调用直接递归调用。,调用过程中,提供给fact的实参按:n,n-1,n-2,,1的规律递减;,调用的返回值却按:1,2*1,3*2*1,(n)*(n-1)*1的规律变化。,设n=3,则上述数据在调用与返回过程中的变化情况如下图所示:,1递归的概念,注意:程序设计语言通常只支持递归调用,C+也不例外。,实体的定义(属性)或实体的处理(操作)包含着实体自身,这种现象称为递归,前者称为递归定义,后者称为递归调用。,比如,例4-9中,fact函
25、数的执行部分包含对自身的调用。,2递归函数的概念,我们把函数的执行部分出现另一函数调用这种现象称为函数的嵌套调用,如果所出现的函数调用是自身的调用,这种特殊的嵌套形式就是函数的递归调用函数直接或间接地调用自身。,3递归调用的基本形式,所谓递归函数即自调用函数,也即在函数体的执行部分中出现函数自身的直接或间接调用。可以看出,递归调用是嵌套调用的特例。,分为直接递归调用和间接递归调用两种形式:,直接递归调用:函数体的执行部分出现本函数的调用。这种调用方式较容易理解。,间接递归调用:其函数体内并没有出现自身的调用,而是出现在被调函数的函数体的执行部分中。请看下面的例子:,4递归调用的条件,运用递归调
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 结构 程序设计 基本 思想 围绕 系统 功能 向下
链接地址:https://www.31ppt.com/p-6332374.html