第4章过程抽象——函数ppt课件_第1页
第4章过程抽象——函数ppt课件_第2页
第4章过程抽象——函数ppt课件_第3页
第4章过程抽象——函数ppt课件_第4页
第4章过程抽象——函数ppt课件_第5页
已阅读5页,还剩62页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四章第四章 过程抽象函数过程抽象函数本章内容n4.1 子程序n4.2 C+的函数n4.3 变量的局部性和变量的生存期n4.4 函数的嵌套调用n4.5 递归函数n4.6 宏定义n4.7 内联函数n4.8 带缺省值的形式参数n4.9 函数名重载基于过程抽象的程序设计 n人们在设计一个复杂的程序时,经常会用到功能分解和复合两种手段:n功能分解:在进行程序设计时,首先把程序的功能分解成若干子功能,每个子功能又可以分解成若干子功能,等等,从而形成了一种自顶向下top-down)、逐步精化step-wise的设计过程。n功能复合:把已有的子功能逐步组合成更大的子功能,从而形成一种自底向上bottom-u

2、p的设计过程。 n过程抽象:一个功能的使用者只需要知道相应功能是什么what to do),而不必知道它是如何做how to do的。4.1 子程序n子程序是取了名的一段程序代码,在程序中通过名字来使用调用它们。n子程序的作用:n减少重复代码,节省劳动力n实现过程抽象功能抽象)n封装和信息隐藏的作用 n语言功能的扩充 4.2 C+函数 函数函数:完成相对独立功能的子程序完成相对独立功能的子程序. 标准库函数标准库函数 (由系统定义,用户可直接调用(由系统定义,用户可直接调用) 自定义函数自定义函数 (用户需要时用户需要时, 自己定义自己定义)C语言函数语言函数4.2.1 C+标准库函数n为了方

3、便程序设计,C+语言的每个实现往往会提供一个标准库,其中定义了一些语言本身没有提供的功能:n常用的数学函数n字符串处理函数以及n输入/输出,等等n在C+标准库中,根据功能对定义的程序实体进行了分类,把每一类程序实体的声明分别放在一个头文件中。n在C+中,把从C语言保留下来的库函数,n重新定义在名空间std中;n对相应的头文件进了重新命名:*.h - c*一些标准数学函数cmath或math.h)nint abs( int n ); /int型的绝对值nlong labs( long n ); /long int型的绝对值ndouble fabs( double x ); /double型的绝对

4、值ndouble sin( double x ); /正弦函数ndouble cos( double x ); /余弦函数ndouble tan( double x ); /正切函数ndouble asin( double x ); /反正弦函数ndouble acos( double x ); /反余弦函数ndouble atan( double x ); /反正切函数ndouble ceil( double x ); /不小于x的最小整数返回值为以n / / double表示的整型数)ndouble floor( double x ); /不大于x的最大整数返回值为以n / double表

5、示的整型数)ndouble log( double x ); /自然对数ndouble log10( double x ); /以10为底的对数ndouble sqrt( double x ); /平方根ndouble pow( double x, double y ); /x的y次幂4.2.1 C+标准库函数库函数使用举例#include /相应的头文件#include /相应的头文件using namespace std; /重新定义在名空间std中;void main() double pi = 3.1415926535;double x, y;x = pi / 2;y = sin( x

6、 );cout sin( x ) = y endl ;y = cos( x );cout cos( x ) = y endl ;4.2.2 自定义 C+函数1.函数的定义: () ; 描述了函数返回值的类型,可以为任意的C+数据类型。当返回值类型为void时,它表示函数没有返回值。用于标识函数的名字,用标识符表示。描述函数的形式参数,由零个、一个或多个形参说明用逗号隔开构成,形参说明的格式为: 4.2.2 自定义 C+函数 为一个,用于实现相应函数的功能。函数体内可以包含return语句,格式为:return ; return;当函数体执行到return语句时,函数立即返回到调用者。如果有返回

7、值,则把返回值带回给调用者。如果return中的的类型与函数 不一致,则进行隐式类型转换,基本原则为:把转成 。留意:在函数体中不能用goto语句转出函数体。4.2.2 自定义 C+函数double max( double x, double y ) if ( x y )return x ;elsereturn y ;函数定义举例:求两个浮点数中较大数4.2.2 自定义 C+函数n每个C+程序都要定义一个名字为main的函数,C+程序的执行是从main开始的。对于函数main,其返回值类型为int,例如:nint main()n.n. return -1;n.nreturn 0;nn一般情况下

8、,返回0表示程序正常结束;返回负数如1表示程序非正常结束。 2. 函数main3.函数的调用 n对于定义的一个函数,必须要调用它,它的函数体才会执行。 n函数调用的格式如下:n()n实参的个数和类型应与相应函数的形参相同。类型如果不同,编译器会试图进行隐式转换,转换规则是把实参类型转换成形参类型 。n留意:不能用goto语句从函数外转入函数体4.2.2 自定义 C+函数4.2.2 自定义 C+函数3. 函数调用举例-1#include using namespace std; float max(float x,float y) /必须分别定义 float z; z=xy?x:y; retur

9、n (z); void main() float a,b; int c; cinab; c=max(a,b); coutMax is cendl; 函数声明 n程序中调用的所有函数都要有定义。n如果函数定义在其它文件如:C+的标准库中或定义在本源文件中使用点之后,则在调用前需要对被调用的函数进行声明。n函数声明的格式如下:n (); /函数原型n或nextern ();n在函数声明中,中可以只列出形参的类型而不写形参名4.2.2 自定义 C+函数4.2.2 自定义 C+函数3. 函数调用举例-2#include #include using namespace std; float max(f

10、loat,float); / 函数声明void main() float a,b; int c; cinab; c=max(a,b); coutMax is cy?x:y; return (z); /file2.cppint g(int i) /定义 extern int x,y; /声明 int z; /定义 z = x + y; return z+i;/file1.cppint x=0; /定义int main() /定义 extern void f(); /声明 extern int g(int); /声明 extern int y; /声明 y = x + 2; f(); /调用 y

11、= g(x); /调用 return 0; int y=0; /定义void f() /定义x = y + 1;4.函数调用的执行过程n计算实参的值对于多个实参,C+没有规定计算次序);n把实参分别传递给被调用函数的形参;n执行函数体;n函数体中执行return语句返回函数调用点,调用点获得返回值如果有返回值并执行调用之后的操作。4.2.2 自定义 C+函数【example4_5】用函数实现求小于n的所有素数。#include #include using namespace std;bool is_prime(int n);/函数声明void print_prime(int n, int c

12、ount);/函数声明int main()int i,n,count=1;cout n; /从键盘输入一个正整数if (n 2) return -1;cout 2 ,; /输出第一个素数for (i=3; in; i+=2)if (is_prime(i)count+;print_prime(i,count);cout endl;return 0;4.2.3 函数的参数传递 nC+提供了两种参数传递机制:n值传递n把实参的值赋值给形参。n地址或引用传递n把实参的地址赋值给形参。nC+默认的参数传递方式是值传递。 值传递n在函数调用时,采用类似变量初始化的形式把实参的值传给形参。n函数执行过程中,

13、通过形参获得实参的值。n函数体中对形参值的改变不会影响相应实参的值。 值参数传递的例子#include #include using namespace std; void swap(int a,int b) int t; t=a;a=b;b=t; couta=a,b=bendl; void main() int x=3,y=4; coutx=x,y=yendl; swap(x,y); coutx=x,y=yendl; 执行执行main时,产生时,产生2个变量分个变量分配内存空间配内存空间x和和y: x: 3 y: 4 调用调用swap函数时,又产生函数时,又产生3个变个变量量a、b和和t:

14、a: 3 b: 4 t: ?函数函数swap中的交换结束后函数中的交换结束后函数返回前):返回前): a: 4 b: 3 t: 3函数函数swap返回后:返回后: x: 3 y: 44.3 变量的作用域及存储分配1.局部变量和全局变量根据变量的定义位置,把变量分成:局部变量和全局变量。 局部变量是指在复合语句中定义的变量,它们只能在定义它们的复合语句包括内层的复合语句中使用。 全局变量是指在函数外部定义的变量,它们一般能被程序中的所有函数使用静态的全局变量除外)。 4.3 变量的作用域及存储分配int x=0; /全局变量全局变量void f() int y=0; /局部变量局部变量x+; /

15、OKy+; /OKa+; /Error全局变量和局部变量的区别全局变量和局部变量的区别全局变量局部变量定义位置函数体外函数体外函数体内函数体内作用域从定义处到本从定义处到本源文件结束源文件结束从定义处到本函数从定义处到本函数结束结束举例所有在函数体所有在函数体外外(1)(1)所有在函数体所有在函数体内(内(2 2)形式参数)形式参数注意和局部变量同和局部变量同名的处理名的处理【举例1:全局变量应用】#include #include using namespace std; int a,b; /a,b为全局变量为全局变量void f1( ) int t1,t2; t1 = a * 2; t2

16、= b * 3; b = 100; coutt1=t1,t2=t2endl; void main( ) a=2; b=4; / 此此a,b是全局变量,赋值是全局变量,赋值 f1( ); / 调用函数调用函数f1( ) couta=a,b=bendl; 【举例2:全局变量和局部变量同名】#include #include using namespace std; int a=2,b=4; /a,b为全局变量为全局变量void f1( ) int t1,t2; t1 = a * 2; t2 = b * 3; b = 100; coutt1=t1,t2=t2endl; void main( ) in

17、t b=4; /同名局部变量同名局部变量 f1( ); couta=a,b=bendl; 全局变量与局部变量同名时,则全局变量与局部变量同名时,则在局部变量的作用范围内,外部在局部变量的作用范围内,外部变量不起作用。变量不起作用。2.2.变量的生存期存储分配)变量的生存期存储分配) (1)把程序运行时一个变量占有内存空间的时间段称为该变量的生存期。 静态:从程序开始执行时就进行内存空间分配,直到程序结束才收回它们的空间。全局变量具有静态生存期 。自动:内存空间在程序执行到定义它们的复合语句包括函数体时才分配,当定义它们的复合语句执行结束时,它们的空间将被收回。局部变量和函数的参数一般具有自动生

18、存期。 动态 :内存空间在程序中显式地用new操作或malloc库函数分配、用delete操作或free库函数收回。动态变量具有动态生存期。具有静态生存期的变量,如果没有显式初始化,系统将把它们初始化成0。 (2)(2)存储类修饰符存储类修饰符 nauto:使局部变量具有自动生存期。局部变量的默认存储类为auto。 nstatic:使局部变量具有静态生存期。它只在函数第一次调用时进行初始化,以后调用中不再进行初始化,它的值为上一次函数调用结束时的值。 nregister:使局部变量也具有自动生存期,由编译程序根据CPU寄存器的使用情况来决定是否存放在寄存器中。2.2.变量的生存期存储分配)变量

19、的生存期存储分配) 存储特性存储特性数据类型数据类型 变量名变量名; ;完整的变量定义完整的变量定义: : 2.2.变量的生存期存储分配)变量的生存期存储分配) nauto型:每次进入程序是自动分配内存,不长期占用内存n 例如:形式参数,自动型局部变量nstatic 型: n 局部静态变量 :静态变量定义在函数中n 全局静态变量:静态变量 定义在函数外n长期占用内存,只进行一次初始化【举例3:变量生存期】#include #include using namespace std; int f(int a)int b=0; static int c=3; b+;c+; coutatbtct; r

20、eturn(a+b+c);void main() int a=2,k; for(k=0;k3;k+) coutf(a) n; f(n);.n如果在一个标识符的局部作用域中包含内层复合语句,并且在该内层复合语句中定义了一个同名的不同实体,则外层定义的标识符的作用域应该是从其潜在作用域中扣除内层同名标识符的作用域之后所得到的作用域。 nvoid f()nint x; /外层x的定义n. x . /外层的xnwhile ( . x .) /外层的xn . x . /外层的x,ndouble x; /内层x的定义n. x . /内层的xnn. x . /外层的xn(2 2全局作用域全局作用域n在函数级

21、定义的标识符具有全局作用域。n全局变量名/对象名、全局函数名和全局类名的作用域一般具有全局作用域,它们在整个程序中可用。 n在局部标识符的作用域中若要使用与其同名的全局标识符,则需要用全局域选择符(:)对全局标识符进行修饰受限)。 4.C+4.C+标识符的作用域标识符的作用域double x; /外层x的定义void f() int x; /内层x的定义. x . /内层的x. :x . /外层的x4.C+4.C+标识符的作用域标识符的作用域(2 2全局作用域全局作用域(3 3文件作用域文件作用域n在全局标识符的定义中加上static修饰符,则该全局标识符就成了具有文件作用域的标识符,它们只能

22、在定义它们的源文件模块中使用。4.C+4.C+标识符的作用域标识符的作用域/file1.cppstatic int y; /文件作用域文件作用域static void f() /文件作用域文件作用域 ./file2.cppextern int y;extern void f();void g() . y . /Error f(); /Error(4 4名空间作用域名空间作用域n对于一个多文件构成的程序,有时会面临一个问题:在一个源文件中要用到两个分别在另外两个源文件中定义的不同全局程序实体如:全局函数),而这两个全局程序实体的名字相同。nC+提供了名空间namespace设施来解决上述的名冲突

23、问题。 n在一个名空间中定义的全局标识符,其作用域为该名空间。n当在一个名空间外部需要使用该名空间中定义的全局标识符时,可用该名空间的名字来修饰或受限。4.C+4.C+标识符的作用域标识符的作用域3、/模块模块2namespace B int x=0;void f() .using A:f;. A:x . /A中的中的x f(); /A中的中的f. B:x . /B中的中的xB:f(); /B中的中的f/模块模块31、2、4.4 函数的嵌套调用n函数的调用是可以嵌套的。 nvoid h()n .nnvoid g()n .n h();n .nnvoid f()n .n g();n .nn直接递归

24、nvoid f()n .n . f() .n .nn间接递归nextern void g();nvoid f()n .n . g() .n .nnvoid g()n .n . f() .n .n1.定义:如果一个函数在定义:如果一个函数在其函数体中直接或间接地其函数体中直接或间接地调用了自己,则该函数称调用了自己,则该函数称为递归函数。为递归函数。4.5 递归函数2.递归调用的条件递归调用的条件递归模型递归模型递归终止条件递归终止条件递归体递归体 确定递归的方式确定递归的方式将一个问题转化成一个新问将一个问题转化成一个新问题,且解题方法相似,每一题,且解题方法相似,每一步转化都越接近终止条件。

25、步转化都越接近终止条件。4.5 递归函数【example4_9】求第n个fibonacci 数递归解法)int fib(int n) if (n = 1 | n = 2) return 1;else return fib(n-2)+fib(n-1); 3.递归函数的执行过程/用递归函数求n! int f(int n) if (n = 0) return 1;else return n*f(n-1);【example4_13】解汉诺塔问题 汉诺塔问题:有汉诺塔问题:有A,B,C三个柱子,柱子三个柱子,柱子A上穿有上穿有n个大小个大小不同的圆盘,大盘在下,小盘在上。现要把柱子不同的圆盘,大盘在下,

26、小盘在上。现要把柱子A上的所有圆上的所有圆盘移到柱子盘移到柱子B上,要求每次只能移动一个圆盘,且大盘不能放上,要求每次只能移动一个圆盘,且大盘不能放在小盘上,移动时可借助柱子在小盘上,移动时可借助柱子C。编写一个。编写一个C+函数给出移函数给出移动步骤。动步骤。A B C分析:n当n=1时,只要把1个圆盘从A移至B就可以了ncout 1:AB endl;n当n大于1时,我们可以把该问题分解成下面的三个子问题:n把n-1个圆盘从柱子A移到柱子C。n把第n个圆盘从柱子A移到柱子B。ncout n B endl;n把n-1个圆盘从柱子C移到柱子B。n上面的子问题1和3与原问题相同,只是盘子的个数少了

27、一个以及移动的位置不同;子问题2是移动一个盘子的简单问题。#include using namespace std;void hanoi(char x,char y,char z,int n) /n个圆盘个圆盘,xy if (n = 1)cout 1: x y yelsehanoi(x,z,y,n-1); /把把n-1个个,x-zcout n : x y yhanoi(z,y,x,n-1); /把把n-1个圆盘个圆盘,z-yvoid main( ) int n; cout n; hanoi(A,B,C,n); 4.递归与循环的选择 n对于一些递归定义的问题,用递归函数来解决会显得比较自然和简洁

28、,而用循环来解决这样的问题,有时会很复杂,不易设计和理解。 n在实现数据的操作上,它们有一点不同:n循环是在同一组变量上进行重复操作循环常常又称为迭代)n递归则是在不同的变量组属于递归函数的不同实例上进行重复操作。n递归的缺陷:n由于递归表达的重复操作是通过函数调用来实现的,而函数调用是需要开销的;n栈空间的大小也会限制递归的深度。 n递归算法有时会出现重复计算。 解决小函数的低效问题n由于函数调用是需要开销的,特别是对一些小函数的频繁调用将使程序的效率有很大的降低。 nC+提供了两种解决上述问题的办法:n宏定义n内联函数 4.6 宏定义 n在C+中,利用一种编译预处理命令:宏定义,用它可以实

29、现类似函数的功能:n#define凵()凵n例如:n#define凵max(a,b)凵(a)(b)?(a):(b)n在编译之前,将对宏的使用进行文字替换!n例如:编译前将把ncout max(x,y);n替换成:ncout (y)?(x):(y);宏定义的不足之处n需要加上很多的括号。例如:n#define max(a,b) ab?a:b n10+max(x,y)+z 将被替换成:n10+xy?x:y+z n有时会出现重复计算。 例如:n#define凵max(a,b)凵(a)(b)?(a):(b)nmax(x+1,y*2)将被替换成:n(x+1)(y*2)?(x+1):(y*2)n不进行参数

30、类型检查和转换。 n不利于一些工具对程序的处理。#include using namespace std;#define ADD(x) x+xvoid main() int m=1,n=2,k=3; int sum=ADD(m+n)*k; coutsum=sumb?a:b;nn内联函数的作用是建议编译程序把该函数的函数体展开到调用点,以提高函数调用的效率。4.7 内联函数 n 使用内联函数时应注意以下几点:n 若inline不在函数名第一次出现时指定,则编译器把它作为普通函数处理。 一般内联函数只适合于1到5行的小程序。 递归函数不能说明为内联函数。 n为了防止同一个内联函数的各个定义之间的不一致,往往把内联函数的定义放在某个头文件中,在需要使用该内联函数的源文件中用文件包含命令#include把该头文件包含进来。由于内联函数名具有文件作用域,因此,不会出现重复定义问题。举例: 从键盘上输入一串字符,以回车结束,统计其中数字字符个数。#include #include inline in

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论