C++ppt课件第3章:函数.pptx

上传人:牧羊曲112 文档编号:1375496 上传时间:2022-11-16 格式:PPTX 页数:89 大小:458.25KB
返回 下载 相关 举报
C++ppt课件第3章:函数.pptx_第1页
第1页 / 共89页
C++ppt课件第3章:函数.pptx_第2页
第2页 / 共89页
C++ppt课件第3章:函数.pptx_第3页
第3页 / 共89页
C++ppt课件第3章:函数.pptx_第4页
第4页 / 共89页
C++ppt课件第3章:函数.pptx_第5页
第5页 / 共89页
点击查看更多>>
资源描述

《C++ppt课件第3章:函数.pptx》由会员分享,可在线阅读,更多相关《C++ppt课件第3章:函数.pptx(89页珍藏版)》请在三一办公上搜索。

1、第三章 函数,东南大学 生物科学与医学工程学院 夏小俊,C+中的函数,3.1 函数的概念与意义,任何c+程序由至少一个函数(function)组成,其中main函数是必不可少的。函数除了是程序的基本组成部分之外,更重要的是可以完成特定的功能。为了完成特定的功能,函数需要数据的输入(参数),并给出数据的结果(返回值)。通过编写和调用函数可以简化程序逻辑,提高编码效率。,子模块1,子模块2,子模块n,功能模块1,功能模块2,功能模块n,软件项目,C程序,C函数,搭积木,系统函数与自定义函数,之前用过的sqrt()等函数并不需要我们去编写和思考实现。类似这样由系统提供的函数,我们称为系统函数或库函数

2、。使用系统函数可以大大提高我们的编程效率,但它并不能覆盖所有的需求。因此我们需要学习自己编写函数,也就是自定义函数。编写自定义函数是结构化(模块化)程序设计的主要步骤。,函数的参数和返回值,回忆之前使用函数的例子,如sqrt()。使用该函数时,需要一个输入的值,并得到对应的输出,如y=sqrt(x)。所以对于函数sqrt,需要一个类型为double的参数(输入),并提供一个类型为double的返回值(输出)。,自定义函数的三部曲,第一个自定义函数max,函数功能:求两个整数当中较大的数。函数逻辑:使用判断语句即可。参数分析:需要两个参数,都是整数类型。返回值分析:结果返回一个整数。,自定义函数

3、的语法格式,返回值类型 函数名(函数参数列表) /函数头/函数体return 返回值;函数参数:数量为任意个,如果为0可以省略;返回值说明:只能为0个或1个,用关键词return实现。如果没有返回值,返回类型用void表示。,第一个自定义函数max,int max(int a , int b) /每一个参数独立 int c = a b ? a : b; return c; /通过该语句返回结果,例:无参无返回值函数print,void print (void) /参数的void可省略 cout * endl; cout *example* endl; cout * endl; /本函数仅仅实现

4、打印的功能,无数据处理,例:求最大公约数的函数gcd,函数功能:求两个整数的最大公约数。函数逻辑:使用递推算法(欧几里得算法)或穷举法皆可。参数分析:需要两个参数,都是整数类型。返回值分析:结果返回一个整数。,递推法实现函数gcd,int gcd(int a , int b) /注意分析返回和参数类型 int c = a % b; /c是a除以b的余数 while(c != 0) /欧几里得算法递推求解 a = b; b = c; c = a % b; return b; /返回最后的结果,穷举法实现函数gcd,int gcd(int a , int b) /与上例相同 int c = a b

5、 ? a : b; /二者中较小的数 while(a % c != 0 | b % c != 0) /尝试 -c; return c; /得到结果,函数定义小结,1、分析清楚函数所需的参数和能提供的返回值至关重要。2、在函数实现过程中需要的辅助变量,不能作为参数来使用,只能定义在函数的内部。3、遇到return语句之后函数自动结束,因此一般将return语句放在函数的最后。4、思考和尝试将常用算法改造为函数实现,如判断某数是否为素数、完全数等,并体会这样做的好处。,3.2 函数的调用和声明,函数的调用过程,主调函数将参数(实际参数)传递给自定义函数,并暂停主调函数的运行;自定义函数根据得到的参

6、数(形式参数)值进行计算,并得出返回值结果;自定义函数将返回值回传给主调函数,结束自定义函数并继续主调函数的运行。,函数的三种调用情况,函数的声明,从结构化程序设计的角度,通常是先调用后定义。而使用函数声明,符合由粗到精的思维方式,又满足了语法要求。函数声明是将函数头添加分号而构成的语句,并且参数名可以省略。部分函数声明示例int max(int a , int b); void print (void) ;int gcd(int , int ) ; /这里省略了参数名,例:函数先定义后调用,int max(int a , int b)int c = a b ? a : b; return c

7、;int main()double x = 5.2 , y = 3.9;cout max(5 , 3); /实参是常量cout max(x , y); /实参是变量,且兼容cout max(x + y , x - y); /实参是表达式且兼容return 0;,int max(int a , int b); /本声明必不可少!int main()int x = 5 , y = 3;cout b ? a : b ; return c; ,例:函数先调用后定义,函数的参数传递(传值调用),两种参数:实际参数(实参)和形式参数(形参)。传递过程是:先计算实参表达式的值,再将该值传递给对应的形参变量。

8、实参和形参的个数和排列顺序应一一对应,并且对应参数应类型匹配(赋值兼容)。实参可以是常量、变量或表达式,形参只能是变量。,实参与形参的单向传递,如前所述,在函数调用的过程中,实参将值传递给形参。形参具备自己的存储空间,和实参的关系是单向传递关系。对形参的赋值不会影响对应的实参!除了这种单向的传值调用之外,第5章还将介绍双向的传引用调用方式。,例:用函数实现数据交换,void myswap(int , int); /全局函数声明int main()int a , b; cin a b;cout main: a b endl;myswap(a , b); /传值cout main: a b end

9、l;,例:用函数实现数据交换,void myswap(int x , int y) cout myswap: x y endl; int t = x; x = y ; y = t; cout myswap: x y endl;,函数myswap运行结果分析,输入两整数:3 5main:3 5 /输出原始的结果myswap:3 5 /实参将值传递给形参myswap:5 3 /形参的交换成功main:3 5 /实参的交换失败!,函数返回值,return 表达式; 用来返回函数的结果。如果该函数的返回类型不为void,那么函数的调用可以组成函数调用表达式参与运算。例:coutmax(a,b); ma

10、x(max(a,b),c);如果该函数的返回类型为void,那么函数的调用只能加上分号成为函数语句。例:print ();,return语句的操作本质,当函数中运行到return语句的时候,将return后面表达式的值传给指定类型的临时变量;临时变量仅在函数表达式内有效,因此函数表达式一般不为左值,如max(a,b) = 2是非法的;如果return后面表达式的类型和函数定义的返回类型不一致,以定义类型为准进行转换。,例:三角形面积求解函数,float TriangleArea(float a, float b, float c)if (a+b=c)|(a+c=b)|(b+c=a) retur

11、n -1;float s=(a+b+c)/2; return sqrt(s*(s-a)*(s-b)*(s-c) ;,int main()float a,b,c,area;coutabc;area=TriangleArea(a,b,c);if(area=-1) cout不能构成三角形!endl;elsecout面积为:areaendl; return 0;,3.3 变量的作用域和生存期,变量除了具备类型、名字和值之外,还有空间和时间上的特性,也被称为变量的作用域和生存期。根据作用域和生存期的不同,可对变量进行不同形式的分类。,C+的内存分布,变量的存储机制(*),代码区(Code area):存

12、放程序代码,即程序中各个函数的代码块;全局数据区(Data area):存放全局数据和静态数据;分配该区时内存全部清零,变量的所有字节自动初始化为零。栈区(Stack area):存放局部变量,如函数中的变量等;分配栈区时不处理内存,即变量取随机值。堆区,自由存储区(Free store area):存放与指针相关的动态数据。,变量的作用域与生存期,C+中的变量除了有数据类型的区别之外,还具备作用域(空间)和生存期(时间)的属性。根据作用域的不同,将变量分为全局变量和局部变量。根据生存期的不同,将变量分为静态变量和动态变量。,全局变量,如果一个变量定义的位置不在任何函数(复合语句)内,则该变量

13、成为全局变量。全局变量的作用域:由定义处开始,到所在文件尾部结束,因此也称为文件作用域。一般情况下,将全局文件定义在文件的开头部分,则所有函数中都可以对全局变量进行读写。,例:全局变量的使用,int n=100;void func()n*=2; int main()n*=2;coutnendl; /输出200func(); coutnendl; /输出400return 0;,全局变量的说明,全局变量可以被多个函数同时使用,因此可以用来做数据交换和共享的用途(例如:用来统计递归函数的运行次数)。全局变量的使用应尽量谨慎,过多地使用容易导致逻辑和流程的混乱。全局变量的存储地点在内存的全局数据区,

14、如不赋初始值则自动初始化为0。,局部变量,之前所有的变量都是局部变量,其特点是变量定义在某个特定的函数内。与函数的情况类似,如果一个变量定义在某复合语句(块)内,则该变量成为此复合语句的局部变量。局部变量的作用域:从变量定义开始,到所在的函数(复合语句)的尾部结束,也称为块域。局部变量的存储地点在内存的栈区。,作用域的覆盖问题,问:属于不同函数的局部变量允许重名吗?全局变量和局部变量可以重名吗?不同层次的局部变量可以重名吗?答:都允许。在作用域覆盖的区域内,遵循“局部优先”的规则。另外,如果局部变量覆盖了全局变量,可以使用域作用符:调用同名全局变量。,例:变量的作用域覆盖,int n=100;

15、int main() int i=200,j=300;cout ntitjendl; int i=500,j=600,n; n=i+j; cout ntitj endl; cout:nendl;/输出全局变量n n=i+j;/修改全局变量cout ntitj endl;return 0;,作用域的总结,根据变量定义时的位置不同,将其分为全局变量和局部变量。全局变量可以被所有函数(块)访问,而局部变量只能在该函数(块)内被访问。全局变量和局部变量可以重名,并遵循局部优先的规则。还有一种作用域:局部声明作用域,其作用范围仅限声明语句内,因此可省略其参数名。,变量的生存期,动态变量:当程序运行至变量

16、所在的函数(块)的时候,系统对其进行空间分配;当所在的函数(块)运行结束时,系统收回其存储空间。之前使用的局部变量一般就属于动态变量。静态变量:当程序开始运行的时候,变量即被创建,并一直生存至程序的结束。之前的全局变量即为静态变量。另可改造普通局部变量的属性,使其成为静态局部变量。 不同生存期的变量,用存储类型的不同来体现。,变量的存储类型,变量定义的完整方式存储类型 数据类型 变量名 = 初始值;C+中支持4种不同的存储类型:auto、register、extern和static。auto类型是局部变量的默认存储类型,extern是全局变量的默认存储类型。说明:最新版本的C+当中对auto的

17、定义已经有所变化!,变量的存储类型,auto:普通的局部变量都属于auto类型,也称为自动变量,属于默认类别因此可省略。例:int c和auto int c是等价的。register:用该类型修饰的变量称为寄存器变量,其使用方式与auto变量几乎一致,区别在于其存储的位置在CPU的寄存器当中。(不可寻址,但已不推荐使用)auto和register变量统称动态变量。,变量的存储类型,static:用static修饰的变量具备静态属性,可分为局部静态变量和全局静态变量。局部静态变量的作用域不变。当其所在的块第一次被执行时,系统在全局数据区开辟其空间,直到整个程序结束才释放。局部静态变量具有局部作用

18、域,但却具有全局生命期。局部静态变量如果不初始化,系统初始化为0。,例:局部静态变量,int st()static int t=100; /仅执行一次!t+; return t;int at()int t=100; /自动变量t+;return t;int main()int i; for(i=0;i5;i+)coutat()t;coutendl;for(i=0;i5;i+) coutst()t;coutendl;return 0;,变量的存储类型,extern:用extern修饰的变量也称为外部变量,是全局变量的默认存储类型。外部变量具备静态变量的生存期属性,即程序开始时分配空间,程序结束时

19、其空间被销毁。这里“外部”的含义是指全局变量可以被同工程下的其他文件调用,其方法是用extern语句进行声明即可。,例:外部变量的使用,文件1,void fun2(); /外部函数声明int n; /全局变量定义int main()n=1;fun2(); / fun2()定义在文件2中coutn=nendl;return 0;,例:外部变量的使用,文件2,extern int n; /外部变量声明void fun2() /fun2()被文件1调用n=3; 运行结果:n=3,外部变量的定义与声明(*),用extern修饰符既可以声明外部变量,也可以定义外部变量。定义外部变量需要分配空间,而声明变

20、量的作用是拓宽其作用域。外部变量的定义和声明的区别(*):跨文件时,省略extern的为定义语句,显式书写的为声明语句;跨文件时,如同时显式书写extern,有初始化的为定义语句,无初始化的为声明语句;在单文件内,也可以用extern声明语句,拓宽外部变量的作用域。,extern的用法对比(*),extern int a = 3; /此处是定义,但不推荐extern int a; /此处是声明,不能单独使用int a; /此处是定义,等价于extern int a = 0int a = 0; /效果同上,静态全局变量,全局变量本已经具备静态属性:存放在全局数据区内,生存期限为整个程序期间,并默

21、认初始化为0等。用static修饰全局变量,其作用是将该变量的作用域限定在文件内,不能为其他文件调用。总结:用static修饰局部变量和全局变量的意义和作用是完全不同的。,作用域和生存期内容总结,根据变量定义的位置,区分为局部变量和全局变量。局部变量属于某个块,全局变量属于某个文件。根据变量的生存期限,区分为动态变量和静态变量。动态变量的生存期为其所属的块的运行期间,而静态变量的生存期则大大延长且默认初始化为0。,作用域和生存期内容总结,局部变量的存储类型属性默认为动态(auto),但可以用static修饰为静态局部变量,其作用是延长其生存期。对静态局部变量的使用理解是本节需要重点考察的内容。

22、全局变量的存储类型属性默认为外部(extern)变量,其性质是静态的且默认可以被别的文件调用。可以用static修饰为静态全局变量,其意义是限定只能在该文件内使用。,作用域和生存期概念总结,3.4 函数的嵌套和递归,在C+中所有的函数地位是平等的,也就是说不允许嵌套定义,即将函数A定义在函数B当中。嵌套调用是允许的,也就是在函数A中调用在函数B。main函数的特权:1)不允许被其他函数调用,2)是程序运行的起点和终点。,函数的递归,如果在定义函数A中的过程中调用函数A,则称为直接递归调用;如果在定义函数A中调用函数B,在定义函数B中调用函数A,则称为间接递归。递归的思想在计算机科学当中非常重要

23、,在生活当中也有着非常广泛的应用,也是哲学中“整体与局部”辩证关系的体现。,递归算法的概念,递归是一种描述问题的方法,或称算法。递归的思想可以简单地描述为“自己调用自己”。,例:用递归来实现阶乘求解,int fac(int n)if (n=0|n=1) /递归终止条件return 1;else return n*fac(n-1); /递归进行条件,fac函数的运行分析,以fac(5)为例:1) n=5,返回的结果为5*fac(4)。在fac(4)得出结果前,函数无法结束,程序转入执行fac(4)。2) n=4, 返回的结果为4*fac(3)。在fac(3)得出结果前,函数无法结束,程序转入执行

24、fac(3)。5) n=1,此时得出结果为1,至此分解部分结束,递归终止。,fac函数的运行分析,6) 在确认fac(1)的值之后,fac(2)的结果也可以确定为2*1=2。7)在确认fac(2)的值之后,fac(3)的结果也可以确定为3*2=6。9)在确认fac(4)的值之后,fac(5)的结果最终确定为5*24=120,此时综合部分结束,运行完毕。,例:递归函数求解费波纳切数列,int fib(int n) /求解第n项的数列值if (n=1|n=2) /递归终止条件return 1;else return fib(n-1)+fib(n-2); /递归条件 /自行尝试分析fib(5)的运行

25、过程,递归函数的使用说明,递归函数的执行分为“递推”和“回归”两个过程。这两个过程由递归终止条件控制,即逐层递推,直至递归终止条件,然后逐层回归。编写递归函数的大致模式:首先判断递归终止条件,如果不终止则展开递归条件。递归函数容易编写且可读性高,但运行效率极低(空间和时间开销都很大),应避免随意使用。,例:用递归函数分解整数,void backward(int n)coutn%10;if(n10) return; else backward(n/10);,例:分解函数的其他写法,void backward(int n)if(n0) coutn%10;backward(n/10);,进一步理解递

26、归,void backward(int n)if(n0) backward(n/10); /这两句的顺序相反coutn%10; /有什么影响吗? /如调用backward(123)就显示123,例:用函数实现进制转化,void trans(int n,int mod)/将n转换为mod进制if(n0) trans(n/mod,mod);couthexn%mod; /假定不超过16进制,例:汉诺塔问题,有A、B、C三根柱子,A柱上有n个大小不等的盘子,大盘在下,小盘在上。要求将所有盘子由A柱搬动到C柱上,每次只能搬动一个盘子,搬动过程中可以借助任何一根柱子,但必须满足大盘在下,小盘在上。打印出搬

27、动的步骤。,例:汉诺塔问题,1、A柱只有一个盘子的情况: A柱C柱;2、A柱有两个盘子的情况:小盘A柱B柱,大盘A柱C柱,小盘B柱C柱。3、A柱有n个盘子的情况:将此问题看成上面n-1个盘子和最下面第n个盘子的情况。n-1个盘子A柱B柱,第n个盘子A柱C柱,n-1个盘子B柱C柱。问题转化成搬动n-1个盘子的问题,同样,将n-1个盘子看成上面n-2个盘子和下面第n-1个盘子的情况,进一步转化为搬动n-2个盘子的问题,类推下去,一直到最后成为搬动一个盘子的问题。,例:汉诺塔问题,1、n-1个盘子A柱B柱,借助于C柱;2 、第n个盘子A柱C柱;3 、n-1个盘子B柱C柱,借助于A柱;其中步骤1和步骤

28、3继续递归下去,直至搬动一个盘子为止。由此,可以定义两个函数,一个是递归函数,命名为hanoi(int n, char source, char temp, char target),实现将n个盘子从源柱source借助中间柱temp搬到目标柱target;另一个命名为move(char source, char target),用来输出搬动一个盘子的提示信息。,void move(char source,char target)coutn;hanoi(n,A,B,C);return 0;,例:递归算法求解公约数,int cgd(int x,int y)if(x%y=0)return y;re

29、turn cgd(y,x%y);,递推算法和递归函数,一般情况下,递推算法都可以改造为递归函数来完成,如求解费波纳切数列或求解公约数。反之则未必可行。递推算法效率极高,但可读性较差;递归函数可读性一般都很好,但运行效率很差。在实际工程应用中,尽量避免使用递归的办法。参考:丰田公司的教训http:/,3.6 函数的其它特性:重载,函数的重载是指一组函数,它们名字相同而参数不同(个数或类型不同)。重载是类的实现当中的必备功能,也是编写参数化和大规模程序的基本要求。仅返回类型不同,不能构成重载。,重载问题的示例,考虑如下的三个函数声明:int sum(int a,int b);double sum(

30、double a,double b);float sum(float a,float b,float c);分析:这三个函数功能相近,所以取了同样的名字;但它们各自的参数不同,这样系统才能决定匹配哪个函数去运行。,函数重载的匹配规则,当某个函数中调用到重载函数时,编译器会根据实参的类型去对应地调用相应的函数。匹配过程按如下步骤进行:1、如果有严格匹配的函数,就调用该函数;2、如果个数相同,类型不同则进行转换,转换时尽量按照类型精度升档进行(*)。3、通过用户定义的转换寻求匹配。,函数重载的匹配选择(*),double sum(double x,double y)coutdouble;retur

31、n x+y;int sum(int x,int y)coutint;return x+y;,函数重载的匹配选择(*),coutsum(1.1f,2.2f); /调用doublecoutsum(1.1,2.2); /调用doublecoutsum(1,2); /调用intcoutsum(a,b);/调用int,函数的其它特性:缺省参数,缺省参数指在定义函数时为形参指定缺省值(默认值)。这样的函数在调用时,对于缺省参数,可以给出实参值,也可以不给出参数值。如果给出实参,将实参传递给形参进行调用,如果不给出实参,则按缺省值进行调用。,例:函数的缺省参数,void delay(int loops=5)

32、/延时函数,默认延时5个时间单位for (; loops0; loops-); int main()delay(3);cout“延时3个时间单位endl;delay();/等同于delay(5)cout“延时5个时间单位endl; return 0;,缺省参数的注意事项,缺省参数可以有多个,但所有缺省参数必须放在参数表的右侧,即先定义所有的非缺省参数,再定义缺省参数。这是因为在函数调用时,参数自左向右逐个匹配,当实参和形参个数不一致时只有这样才不会产生二义性。在同一个作用域中一个参数只能被指定一次缺省值。如果同时存在声明和定义,则缺省值只能写在声明当中(定义时不能再写,即使一样也不行)。,函数

33、的其他特性:内联函数,函数的定义降低了程序的逻辑复杂性,是结构化程序设计的基本元素。但函数的调用需要一定的时间和空间开销,在运行效率上略有损失。如果一个函数的定义较简单且被多次调用的话,就可以尝试将其定义为内联函数。其意义是编译系统将函数的调用语句替换为函数的执行内容,从而省去了调用的开销。如果函数无法直接执行,即使定义为内联函数,也是无效的。,内联函数示例,inline IsNumber(char ch) return ch=0,3.7 编译预处理,CPP程序编写完毕之后,要经过编译和链接两个步骤才能运行。在编译之前完成的步骤就称为编译预处理,C+中的编译预处理有三种:头文件包含、宏定义和条

34、件编译。编译预处理指令以#开头,且不是C+中的语句。,宏定义指令,宏定义指令的基本格式#define 宏名 常量串宏定义的意义:将代码中所有出现宏名的地方,用常量字符串进行替换,并得到一个中间文件供编译程序处理。例:#define PI 3.1415926s = PI * 2 * 2;则编译时的语句是s = 3.1416926 * 2 *2;,宏定义和普通变量的区别,问题:#define PI 3.1415926 和 double PI = 3.1415926的区别在哪里?解答:宏定义只是文本的替换,不需要经过任何的检查,而变量定义必须考虑类型和格式的问题。例如, #define PI 3.a

35、4b5c26 这样的写法也是完全可以的!当然这样写完之后了无法使用,但并不是在宏定义环节发生的错误。,宏定义的嵌套和带参数的宏,1、宏定义可以嵌套定义。例:#define PI 3.14 #define R 2#define AREA PI*R*RcoutAREA; /结果为12.562、宏定义可以写成类似函数的格式例:#define AREA(C,H) 0.5*C*HcoutAREA(3,4); /结果为6,宏定义的嵌套和带参数的宏,使用这两种宏时,需要注意宏本身的意义。如coutAREA(1+2,2+2); 输出为6.5!因为其处理过程为0.5*1+2*2+2=6.5!为了避免上述问题,可以将参数用括号保护。如#define AREA(C,H) 0.5*(C)*(H)提问:上述办法就足够了吗?如要计算12/AREA(3,4),其值为多少?为什么不合理?如何解决?,宏定义的总结,宏定义主要用来表示代码中用到的常量,因此也称为“符号常量”。在系统库文件中,宏定义被广泛使用。宏定义只是字符串的替换,千万不要和赋值、传参、计算等过程混淆。为了和普通变量区分,宏定义一般都用大写表示。,

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

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


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号