第6章函数.ppt
6.1 求周长与面积 6.2 验证歌德巴赫猜想 6.3 求最大公约数 6.4 Fibonacci数列 6.5 编译预处理 6.6 综合应用实例,第6章 函数,6.1 求周长与面积,6.1.1 程序解析 例6-1 输入圆的半径,计算并输出圆的周长及面积,分析】 1包含计算圆的周长和圆的面积两个问题,分别采用子函数perimeter和area实现。 2在main主函数中,先定义变量,输入圆的半径,调用子函数计算周长与面积,输出结果,include const float PI3.14159 void main float s,l r; printf“请输入圆的半径 ”; scanf“f”,float perimeterfloat r return 2*PI*r; float areafloat r float s; sPI*r*r; return s;,lperimeterr; sarear,k nm,f1; fori1; in; i ff*i,scanfddd,函数功能独立,反复使用的代码段 1一个源程序文件由一个或多个函数构成。 2程序是由一个或多个源程序文件组成,以文件为单位进行编译。 3程序的执行从main函数开始,在main中结束。 4函数不能嵌套定义,可相互调用,但不能调用main函数,6.1.2 程序的结构,1. 从函数定义的角度 1标准函数,即库函数。 2用户自定义函数。 2. 从函数参数的角度 1无参函数。函数定义、说明、调用均不带参数。 2有参函数。函数定义、说明时有形参,调用时有实参。 3. 函数分类的其他角度 1 返回值有返回值和无返回值函数; 2 返回值类型整型、实型、字符型和指针型函数等,6.1.3 函数的分类,6.1.4 函数的定义,1.无参函数 类型 函数名 声明部分 执行部分,类型和函数名 称为函数头。类型是指该函数返回值类型,系统隐含为int。函数名是用户定义的标识符,函数名后面必须有一对括号 。花括号 中的内容称为函数体,由声明和执行语句组成,函数运算结果通过return语句返回. return 表达式;或 return 表达式,例6-2】求两个整数较大值无参无返回值函数,void main max1 ;,includevoid max1 int a, b, c; scanfd,d,输入、输出和计算操作均在子函数中完成,无参函数一般没有返回值,类型为void,即空类型,以明确函数调用结束时无返回值,例6-2】求两个整数较大值无参有返回值函数,void main int m; mmax2 ; printfmaxdn, m;,includeint max2 int a, b, c; scanfd,d,输入和计算操作在子函数中完成;输出在主函数中,类型 函数名类型 形参1,类型 形参2, 声明部分语句; 执行部分语句;,2.有参函数,在函数头部增加形参类型说明内容。形参可以是任何类型,参数间用逗号分隔。在函数调用时,主调函数将实际参数值赋给这些形参变量,例6-2】求两个整数较大值有参无返回值函数,void main int a, b; scanfd,d,includevoid max3int x, int y int z; zxyxy; printfmaxdn, z;,计算和输出操作在子函数中完成;输入在主函数中,例6-2】求两个整数较大值有参有返回值max函数,void main int a, b, m; scanfd,d,includeint max4int x, int y int z; zxyxy; returnz;,计算操作在子函数中完成;输入输出在主函数中,include void pyramidint n int i,j; fori1; in; i forj1;jn-i;j printf“ ”; forj1;ji;j printf“d ”,i; printfn; void main int n; scanfd,例6-3】编写函数,输出n以内的数字金字塔,分析 函数体为图形输出,设置函数为void类型 形参为整型变量n,空函数”定义的一般形式 类型说明符 函数名 例如 void khs 主函数中可有调用语句khs; 但没有作用;“空函数” 一般为今后程序中增加功能而预留位置,3. 空函数,函数的首部写成不止一行的形式。 注在VC环境下,扩展名必须是.c格式,否则编译出错 类型说明符 函数名形参1,形参2,类型 形参1,类型 形参2,; 说明部分语句; 执行部分语句;,4.传统风格的函数定义,例6-2 max2可以写成 int max4x, y int x, y; int z; zxyxy; returnz;,6.1.5 函数设计的基本原则,1、函数规模要小,尽量控制在50行代码以内。 2、函数的功能要单一,不要让它身兼数职。 3、每个函数只有一个入口和出口。 4、在函数入口处清楚地定义函数的行为。 5、在函数入口处对参数的有效性进行检查。 6、当函数需要返回值时,应确保函数中的所有控制分支都有返回值;函数无返回值时需用void声明,6.2 验证哥德巴赫猜想,6.2.1 程序解析 例6-4 验证哥德巴赫猜想,输入一个大于6的偶数,要求验证其等于两个素数之和,分析 对于大于6的偶数s,利用循环测试是否存在a2as/2和s-a为素数,若存在这样的a则验证猜想并输出结果。 素数判断可以考虑定义子函数prime,函数的形参为n,功能是若n为素数则返回1,否则返回0,include int primeint n int i; if n1 return 0; for i2; in; i ifni0 return 0; return 1; void main int s, a; int prime int n; scanfd,6.2.2函数调用和参数传递 1.函数调用 函数名实际参数表 【说明】多个实参用逗号分隔; 实参与形参个数相同,类型对应; 调用无参函数,园括号 不能省略,void main int s,a; primea;,int primeint n,调 用,返 回,函数调用流程,为被调函数局部变量含定义变量和形参开辟存储单元。 将实参的值复制给形参。 流程从主调函数的调用处转移到被调函数,执行被调函数体中语句序列。 当执行到被调函数“return 表达式;”语句时,返回到主调函数,将表达式的值作为函数值替换“函数名实际参数表”,结束被调函数的执行;如无return语句,则遇函数体右花括号“”结束被调函数的执行。 释放局部变量,流程从被调函数转移到主调函数调用处。 继续从主调函数的调用处向下执行,函数调用流程的执行过程,2.函数调用的应用形式 1无返回值函数调用语句pyramidn 2表达式if primea ifab c1; else ifab c0; returnc;,include void main int k1,j; jfk,k; printfdn,j;,return表达式;或 return 表达式; 说明1、计算表达式的值,并作为函数值返回给主调函数。 2、一个函数中允许有多个return,但每次调用只执 行一个 return语句,只能返回一个函数值,6.2.3 函数返回值,说明 1 对于void型函数,return语句后没有表达式; 2 如果不需要返回函数值,即对于void型函数中可以没有return语句,程序执行到函数体的右括号“”时自动返回。 3 函数返回值的类型和函数定义的类型应保持一致。如果不一致,以函数定义类型为准。 4 在标准C中,返回值默认int型 ,在函数定义时可省略int,6.2.4 函数的声明,函数调用条件 1该函数必须存在; 2自定义函数调用时一般要声明; 3调用库函数时,要include命令,声明形式 1 类型说明符 函数名类型 形参1,类型 形参2,; 形参变量名可省,也可与定义时不一致,如 int max4 int , int ; 2 类型说明符 函数名 ; /只对标准C文件*.c有效,对被调函数的声明省略条件 1函数定义在前,调用在后; 2在函数外部已声明; 3整型函数,在.c中可以不声明,但.cpp中要声明,include void main int a,b; float averint x, int y; float ave; scanfd,d,例6-6】对被调函数的声明,6.3.1 程序解析 例6-7 求两个自然数的最大公约数和最小公倍数,分析 定义最大公约数函数int gcdx,y 求最大公约数有三种算法穷举法,欧几里得算法和递归算法。 定义求最小公倍数 lcmx,yx*y /gcdx,y,6.3 求最大公约数,include void main int gcdint a,int b,lcmint,int; int x,y,g,l; printfplease two integers ; scanfdd,int gcdint x,int y /穷举法 int i; ifx1; i- if xi0,嵌套调用gcd函数,穷举法 测试i从大x和y的较小数到小1,判断其是否能同时被x和y整除,如果成立i就是x和y的最大公约数,否则i减1后继续循环判断,int gcdint x,int y /欧几里得法 int r; whiley0 rxy; xy; yr; return x;,int gcdint x,int y int r; do rxy; xy; yr; whiler0 return x;,欧几里得算法 辗转相除求余数对于给定的两个整数,用较大的数除以较小的数,若余数不为0,则将余数和较小的数构成一对新数,继续进行上面的除法运算,直到大数被小数除尽,这时较小的数就是最大公约数,int gcdint x,int y int r; while rxy0 xy; yr; return y;,int gcdint x,int y int r; rxy; while r0 xy; yr; rxy; return y;,int gcdint x,int y ifxy0) return y; else return gcdy,xy;,递归算法,6.3.2 函数嵌套调用 函数的定义是相互平行、相互独立。函数不能嵌套定义,但可以嵌套调用,如求最小公倍数时嵌套调用了求最大公约数 【例6-8】分析下面程序,其功能是通过函数计算s12310,double sumint m double s0, mulint ; int i; fori1;im;i ss muli; return s;,double mulint n double t1, j; for j1;jn; j tt*j; return t;,include void main double s , sumint; ssum 10; printf s.0fn,s;,定义一个求和函数sum实现a1a2am。 定义一个求阶乘的函数mul实现1*2*n,该程序中,主函数调用了sum函数,而在sum函数中又调用了mul函数。在一个函数被调用的过程中又调用另一个函数,这就是函数的嵌套调用。本例函数嵌套调用的执行过程如图所示,递归调用在函数调用过程中直接或间接调用自己。 直接递归函数不断直接调用自己; 间接递归函数循环间接调用自己,6.3.3 函数的递归调用,编写递归函数必须有两个条件 1 有递归调用的形式,即形式上要能自己调用自己; 2 必须有终止递归调用的条件,利用递归函数解决问题的优点 1递归调用的算法自然、容易理解; 2采用递归算法的程序简单; 3解决用其他方法无法解决的问题如汉洛塔问题,n 123n123n-1n nn-1 也就是说,要求n可先求n-1。同样,要求n-1先求n-2,.,要求2先求1。而11 , 01。则有,第一行是递归的终止条件,第二行是递归调用的形式,例6-9】用递归算法求n123n,void main int m; long f; do printf输入m m0; scanfd,include long factint n ifn0 | n1 return 1; else return n*factn-1;,该程序中,主函数调用了fact函数,而在fact函数中又调用了自身fact函数。程序执行流程如图所示,从图中可以看出,fact函数每自我调用一次就进入新的一层,递归函数和变量都在内存新的空间开辟,6.4 Fibonacci数列,6.4.1 程序解析 例6-10 用递归方法输出Fibonacci数列的任一项,同时输出计算Fibonacci数列某一项时所需的递归调用次数,分析 递归出口 n1或2) F1F21 递归形式FnFn-1Fn-2(n3 定义fibn函数,返回数列的第n项。 递归次数全局变量count(一次函数调用只能返回一个结果,include int count0; /全局变量 int fibint n int f; count; if n1|n2 f1; else ffibn-1fibn-2; return f;,void main int n,x; printfplease n ; scanfd,6.4.2 变量的作用域 作用域是指变量的有效范围, 分局部变量和全局变量,1.局部变量 局部变量函数形参、函数内部和复合语句内定义的变量。 作用域仅限于函数内或复合语句内。 注意 在不同函数内可以定义同名变量,同名变量之间不冲突。 若变量名相同,则小范围内变量比大范围内变量优先。局部变量所在局部被执行时,才分配内存,调用结束立即释放,例6-11】复合语句中的局部变量,include void main int t10; int t20; printfindn,t; printfoutdn,t;,分析若内部大括号去掉,结果如何,int t20; printfindn,t,全局变量在所有函数含main函数之外定义,又叫外部变量 作用域从定义开始到本程序文件结束,默认初值为0,2.全局变量,注意 1 全局变量定义在所有函数之外,且只能定义一次。 类型 变量名1,变量名2,变量名n; 2 在编译时分配内存空间,系统自动赋0值 3 全局和局部变量同名时,在局部变量有效的范围内,全局变量不起作用,写程序运行结果,include int a5; void funn printf2 adn,a; int a15; printf3 adn,a; void main int a10; printf1 adn,a; funn; printf4 adn,a;,变量的存储方式(生存期)是指变量使用内存空间的方式,分为“动态存储”和“静态存储”两种。 动态存储用时分配,用完释放。(局部变量) 静态存储编译时分配,整个程序结束时释放。(全局变量) 生存期和作用域是从时间和空间两个不同的方面描述变量的特性,两者既有关联,又有区别。变量的存储类型决定了变量的生存期和作用域,6.4.3 变量的存储属性,1.自动类型在函数内部定义的变量以前所用的变量、形参 auto 类型 变量名1, 变量名2, 变量名n; 1自动变量属于动态存储方式,用时分配,用完释放。 2自动变量为局部变量,只在定义的函数或复合语句中起作用,2.寄存器类型 register 类型 变量名1, 变量名2, ,变量名n; 1 寄存器变量属于动态存储方式。 2生存期和作用域与自动类型变量相同,系统将这类变量分配在CPU的通用寄存器中。只能通过赋值语句赋值,没有地址,不能用scanf语句输入其值,3.静态变量 static 类型 变量名1 , 变量名2, , 变量名n; 静态变量是静态存储的,具有永久生存期,自动赋初值0。 1静态局部变量 函数内部定义。只在定义它的函数中起作用,其初始化语句只在第一次运行时起作用。 变量值始终存在,当函数被调用结束后,静态内部变量继续保存其值。 2 静态全局变量 函数外部定义,只在当前源文件的定义处到文件末尾起作用。 在当前源文件中可用,在同一源程序其他文件里不能使用,例6-12】静态局部变量的继承性,include void fun static int a0; a2; printf2d,a; void main int n; for n1;n3;n fun ; printfn;,4.外部变量(在函数外部定义) 类型 变量名1, 变量名2, 变量名n; 从定义处到文件尾所有函数内有效,函数之间通过外部变量可以共享数据。 若要在同一程序的其他源文件或定义前使用该外部变量,则要用extern存储类型进行声明。 extern 类型 变量名1, 变量名2, 变量名n,例6-13】写程序运行结果,include int a, b5; void main int a10; void subb; subb; printfmain_1ad, bdn,a,b; extern int a; subb ; printfmain-2ad, bdn,a,b; void subb aa1;bab; printfsubbad, bdn,a,b;,6.4.4 内部函数和外部函数,1.内部函数 只供本文件中其他函数调用,又称静态函数。 static 类型说明符 函数名形参表,2.外部函数 定义函数时,没有static,或加extern,则表示是外部函数,可供同一程序其他文件中的函数调用。 extern 类型说明符 函数名形参表 函数的外部性是函数的默认属性,一般不需要特别说明,而内部函数却是需要说明的,3.一个程序多个源程序文件的使用 1方法一用include预编译命令,每个源程序文件都可以包含其他源程序文件,被包含的文件在编译时代码将会被放在该命令处一般方法。 2方法二在集成环境中使用project菜单。 a、建立.PRJ工程文件,该文件的名字通过菜单项Project name输入,在编辑窗口输入内容每行一个源文件名。编译、连接、运行该文件即可。 b、由于工程文件是默认执行的文件,运行后记住删除Project name菜单中的文件名,6.5 编译预处理,语言有三种预处理宏定义、文件包含和条件编译 预处理的命令特点 1 以开头,结尾不加分号; 2 放在函数外任何位置,作用范围从定义处到文件结尾,6.5.1 宏定义命令 1无参宏定义 define 宏名 替换文本 例如 define PI 3.14 1宏名用户标识符,一般用大写字母。 2替换文本要替换宏名的一些字符。 3作用从本行以下凡出现宏名的地方都用替换文本替换,直到出现undef命令为止,1宏名与替换文本之间有空格,但宏名中不能有空格; 2替换文本中可包含已定义过的宏名,如 define PI 3.14 define ADDPI PI1 define TWO_ADDPI 2*ADDPI 表达式 xTWO_ADDPI/2 替换成 x2*3.141/2 3注意给替换文本适当加上括号。因为是原样替换,不加括号会出错,如上面两行不加括号,表达式将变成x2*3.141/2。 4替换文本续行用表示,但要注意其前和下行行首不能有空格,否则将成为替换文本的内容。 5宏名是标识符,不能用引号括起,也不能重复使用。 6程序中与宏名相同的字符串不是标识符,不予替换;其它标识符中含有宏名字符,也不予替换。 7 一般将宏替换放在程序开头,注意事项,define 宏名形参表 替换文本 1形参为标识符,多个形参时用逗号隔开 2替换文本中应包含形参 宏调用形式 宏名实参表 作用从本行起以下“宏名实参表” 字符串中除外都用替换文本替换,替换文本中的形参换成实参,2.有参数的宏定义,注意事项】1宏名要紧跟圆括号 2替换文本及形参一般要用括号括起来 3实参可以是常量、变量或表达式。 4宏名不能重复定义。 5替换文本中与形参相同的字符串不被实参替换 6不能递归定义宏替换,1带参宏只是依样替换,没有函数功能,替换后的源程序才有功能。 2带参数宏替换对实参没有类型要求。 3函数调用在程序运行时进行,并动态分配存储单元;带参数宏替换在编译预处理时进行,不分配存储单元,无值传递,也无值返回。 4带参数宏替换不占用运行时间但占用编译时间。 带参数宏替换的用途 1一次定义多次调用,减少源代码; 2每次可使用不同数据类型调用; 3系统头文件大多用宏替换编成,带参数宏替换与函数调用的区别,例6-14】计算1到10 的平方和以及1.1、1.2到2.0的平方和,define SQRx x*x include void main int i, s10; float s20; fori1;i10;i s1SQRi; s2SQRi/10.01; printf1*12*2.10*10dn,s1; printf1.1*1.11.2*1.2.2.0*2.0.2fn,s2;,格式】undef 宏名 【作用】取消定义的宏,以后可以将宏名作其它用途,如 define OK 1 /*定义OK为1 */ undef OK /*取消定义OK */ define OK 5 /*重定义OK为1 *,3.宏终结命令undef,include 直接在选项Include目录中查找,标准方式 include “文件名” 先在当前目录中查找,再用标准方式查找 说明 1include命令写在文件开头,被包含文件称为“头文件”,扩展名可不用“.h”。文件名中可以使用文件路径。 2一个include命令只能包含一个文本文件。一个程序允许有多个include 命令行。被包含文件可嵌套包含其它文件。 3被包含文件修改后,源程序要重新编译。 4可用include命令将多个源文件连接起来,形成一个文件,6.5.2文件包含命令,6.5.3 条件编译命令,1. 第一种形式 ifdef 标识符 程序段1 else 程序段2 endif,2. 第二种形式 ifndef 标识符 程序段1 else 程序段2 endif,3. 第三种形式 if 常量表达式 程序段1 else 程序段2 endif,例6-15】根据下式用递归算法求的近似值,分析】设级数和为sn,根据上式有,6.6 综合应用实例,include void main int n; double sint n; printf请输入 n; scanfd,double sint n if n1 return2.0/3; else return1.0/4*n-3-1.0/4*n-1sn-1;,例6-16】Hanoi汉诺塔问题) 古代有一个梵塔,塔内有三各底座A、B、C。开始时A座上有64个盘子,牌子大小不等,大的在下,小的在上。有一个老和尚想把这64个盘子从A座移到C座上去,每一次只允许移动一个盘子,且在移动过程中每个底座上的盘子必须是大的在下,小的在上,老和尚只需这样做 (1)命令第二个和尚将63个盘子从A移到B座; (2)自己将最底下最大的那一个盘子从A移到C座; (3)再命令第二个和尚将63个盘子从B移到C座; 为完成第一步操作,第二个和尚这样做 (1)命令第三个和尚将62个盘子从A移到C座; (2)自己将最底下的最大的那一个盘子从A移到B座; (3)命令第二个和尚将62个盘子从C移到B座; 这样以此类推,层层下放,直到第63个和尚找到第64个和尚,第64个和尚的任务是将一个盘子移到另一座上,第63个和尚再完成自己的任务;第62个和尚再完成自己的任务;老和尚才能完成自己的任务。 3个盘子的步骤为A-C, A-B, C-B, A-C, B-A, B-C, A-C,include void movechar getone, char putone printfc-cn,getone,putone; void hanoiint n,char one,char two,char three ifn1 moveone,three; else hanoin-1,one,three,two; moveone,three; hanoin-1,two,one,three; void main int m; printf the number of disks; scanfd,例6-17】分析下面程序,理解静态变量和全局变量,include int x3; /全局变量 void main void cre; int k; fork1; kx; k cre; printf“n”;,void cre static int x1;/静态局部变量 x*x1; printf“d”,x;,全局x,局部x,继承,复习,一、程序填空题(每空5分,共30分) 二、程序改错题(在标有/*ERROR*/的行有错,每处5分,共30分) 三、编程题(共40分) 考核知识点分支、循环、函数,选择结构,if 语句 switch 语句 例1输入三个数,求最大数 例2分段函数的计算 例3输入一个十进制数,输出所对应的英文星期单词,循环结构,for语句,while语句,do while语句 例1表达式求和。输出2/13/25/38/5的前十项之和,结果保留2为小数。 例2穷举算法。百钱百鸡问题 例3迭代算法。 例4图形输出,函数,定义、声明、调用 例1素数prime,水仙花数narcissus 完全数perfect 6123,奇数odd 例2递归函数。最大公约数,n,整数的各位数字之和