2020/1/19华中科技大学计算机学院1第5章函数与程序结构本章内容:构化编程和C程序的一般结构。函数的机制:包括函数定义、函数声明、函数调用、变量的存储类型、参数数目可变的函数等。递归与回溯:包括解释递归与回溯的概念、递归函数设计,以及递归调用。多文件程序设计。2020/1/19华中科技大学计算机学院25.1C程序的一般结构5.1.1结构化程序设计结构化编程是一种解决问题的策略,它包括如下2条编程标准:(1)程序中的控制流应该尽可能简单。(2)应该自顶向下地设计程序结构。自顶向下设计也称为逐步细化,即把一个问题按功能分解为若干子问题,如果子问题还较复杂,可将其继续分解,直到分解成为容易求解的子问题为止。分解而来的每个子问题被称为模块,C中提供的函数机制完成每个模块的编程任务,即用函数编写由分解而来的子问题的代码。2020/1/19华中科技大学计算机学院3例显示从1到10的整数幂。***************************************ATABLEOFPOWERS***************************************IntSquareCubeQuarticQuintic111112481632392781243416642561024525125625312563621612967776749343240116807864512409632768981729656159049101001000100001000002020/1/19华中科技大学计算机学院4自顶向下的分解问题:(1)显示标题(2)显示表头(3)分列整齐地显示整数1到10的2至5次幂每个子问题都能作为函数直接被编写成代码,在main中调用这些函数,解决总的问题。2020/1/19华中科技大学计算机学院5#includestdio.hvoidprn_banner(void);/*prn_banner的函数原型*/voidprn_headings(void);/*prn_headings的函数原型*/doublepower(intx,intn);/*power的函数原型*/voidmain(void){inti,j;prn_banner();/*显示标题*/prn_headings();/*显示表头*/for(i=1;i=10;i++){printf(%5d,i);for(j=2;j=5;j++)printf(%10.0f,power(i,j));/*ij*/printf(\n);}}main函数:2020/1/19华中科技大学计算机学院6结构化程序设计的益处使程序编制方便,易于管理、修改和调试。增强了程序的可读性、可维护性和可扩充性,方便于多人分工合作完成程序的编制。函数可以公用,避免在程序中使用重复的代码。提高软件的可重用性,软件的可重用性是转向面向对象程序设计的重要因素。2020/1/19华中科技大学计算机学院75.1.2C程序的一般结构C程序由一或多个函数组成,这些函数可以编辑成多个C源文件,每一个C源文件含有一个或多个函数定义。各C源文件中要用到的一些外部变量说明、枚举类型声明、结构类型声明、函数原型和编译预处理指令等可编辑成一个.h头文件,然后在每个C文件中包含该头文件。每个源文件可单独编译生成目标文件,组成一个C程序的所有源文件都被编译之后,由连接程序将各目标文件中的目标函数和系统标准函数库的函数装配成一个可执行C程序。除main以外的其它函数分两类,一类是由系统提供的标准函数。另一类是需要由程序员自己编写的函数(“自定义函数”)。2020/1/19华中科技大学计算机学院85.2函数的定义与函数的声明程序中若要使用自定义函数实现所需的功能,需要做三件事:①按语法规则编写完成指定任务的函数,即定义函数;②有些情况下在调用函数之前要进行函数声明;③在需要使用函数时调用函数2020/1/19华中科技大学计算机学院95.2.1函数的定义函数定义的一般形式为:类型名函数名(参数列表){声明部分语句部分}无返回值,类型名用void。无参数,参数列表void(可缺省不写)2020/1/19华中科技大学计算机学院10函数定义的例子/*函数prn_banner:显示标题*/voidprn_banner(void)/*同于voidprn_banner()*/{printf(\n%s%s%s\n,*******************************\n,*ATABLEOFPOWERS*\n,*******************************\n);}2020/1/19华中科技大学计算机学院11未指明函数返回值的类型,默认为int/*函数prn_headings:显示表头*/voidprn_headings()/*不同于prn_headings()*/{printf(\n%5s%10s%10s%10s%10s\n,Int,Square,Cube,Quartic,Quintic);}2020/1/19华中科技大学计算机学院12必须明确地列出每一个参数的类型。函数定义中的参数称为形式参数(形参)/*函数power:计算xn,n0*/doublepower(intx,intn)/*不能为doublepower(intx,n)*/{inti;doublep;for(i=1,p=1;i=n;i++)p*=x;returnp;}2020/1/19华中科技大学计算机学院135.2.2return语句return语句可以是如下两种形式之一:(1)return;/*void函数*/(2)return表达式;/*非void函数*/void函数也可以不包含return语句。如果没有return语句,当执行到函数结束的右花括号时,控制返回到调用处,把这种情况称为离开结束。表达式值的类型应该与函数定义的返回值类型一致,如果不相同,就把表达式值的类型自动转换为函数返回值的类型。2020/1/19华中科技大学计算机学院14函数返回的值,程序可以使用它,也可以不使用它while(…){getchar();/*返回值不被使用*/c=getchar();/*返回值被使用*/…}2020/1/19华中科技大学计算机学院15一旦return被执行,控制立即返回到调用处,其后的代码不可能被执行/*is_prime:如果n是素数,则返回1;否则,返回0*/intis_prime(intn)/*等价于is_prime(intn)*/{intk,limit;if(n==2)return1;if(!(n%2))return0;limit=n/2;for(k=3;k=limit;k+=2)if(!(n%k))return0;return1;}2020/1/19华中科技大学计算机学院165.2.3函数的声明1.函数定义起函数声明的作用函数定义在前,调用函数在后。例:#includestdio.hvoidprn_banner(void){…}voidprn_headings(void){…}doublepower(intx,intn){…}voidmain(void){…}2020/1/19华中科技大学计算机学院172.函数原型函数定义出现在函数调用后被调用函数在其它文件中定义必须在函数调用之前给出函数原型。2020/1/19华中科技大学计算机学院18函数原型的一般形式类型名函数名(参数类型表);参数类型表通常是用逗号隔开的形参类型列表,而形参名可以省略。例如,doublepower(int,int);等价于doublepower(intx,intn);无参函数的函数原型参数表必须指定为void2020/1/19华中科技大学计算机学院19函数原型的作用函数原型告诉编译器函数返回值的数据类型,传递给函数的参数个数、参数类型和参数顺序。编译器用函数原型校验函数调用,强制转换传递的参数类型,从而避免错误的函数调用导致的致命运行错误或微妙而难以检测的非致命逻辑错误。例如,printf(%.1f\n,sqrt(4));(1)包含了math.h,输出2.0*/(2)没有包含math.h,函数调用sqrt(4)不会产生正确的值。2020/1/19华中科技大学计算机学院203.遗漏函数原型编译器首次遇到没有声明的函数调用时,将构造一个缺省声明并继续编译:intf();/*函数是int类型,但不给出参数表信息*/(1)函数类型不是int,给出一个错误提示:Typemismatchinredeclarationof'f‘(2)函数类型是int,就不给出错误提示。编译器对参数类型不作检查,把正确类型的参数传递给函数是程序员的责任。假设函数f只有一个类型为int的参数,函数调用f(2)会产生正确的值,而函数调用f(2.5)将产生错误的运行结果。2020/1/19华中科技大学计算机学院21良好的编程风格在函数调用之前必须给出它的函数定义、函数原型或两者都给出。引入标准头文件的主要原因是它含有函数原型。2020/1/19华中科技大学计算机学院225.3函数调用与参数传递5.3.1函数调用1.函数调用的形式函数名(实参列表)实参是一个表达式,对于无函数,调用形式:函数名()2020/1/19华中科技大学计算机学院23函数调用在程序中出现的形式(1)作为表达式语句出现。prn_headings();putchar(c);(2)作为表达式中的一个操作数出现。例如:c=getchar()sum+=power(i,j);(3)作为函数调用的一个实参出现,即嵌套调用。printf(%10.0f,power(i,j));putchar(getchar());2020/1/19华中科技大学计算机学院242.函数调用的执行过程#includestdio.hintmax(int,int);voidmain(void){intm,a=3,b=4;m=max(a,b);printf(%d,m);}intmax(intx,inty){if(xy)returnx;elsereturny;}2020/1/19华中科技大学计算机学院253.实参的求值顺序实参的求值顺序由具体实现确定,有的按从左至右的顺序计算,有的按从右至左的顺序计算a=1;max(a,a++)----从左至右:max(1,1)----从右至左:max(2,1)(多数)为了保证程序清晰、可移植,应避免使用会引起副作用的实参表达式.2020/1/19华中科技大学计算机学院26实参的求值顺序C语言采用从右至左规则#includestdio.hvoidmain(void){intx=1;printf(%d\t%d\t%d\n,x,x++,x);}运行结果:2112020/1/19华中科技大学计算机学院275.3.2参数的值传递参数的传递方式是“值传递”,实参的值单向传递给相应的形参。如果实参、形参都是x,被调用函数不能改变实参x的值。2020/1/19华中科技大学计算机学院28例说明值传递概念的程序#includestdio.hlongfac(int);voidmain(){intn=4;printf(%d\n,n);/*输出4*/printf(%ld\n,fac(n));/*输出24*/printf(%d\n,n);/*输出4*/}longfac(intn)/*计算n的阶乘*/{longf=1;for(;n0;--n)f*=n;/*main中的n值未改变*/printf(%d\n,n);/*输出0*/return(f);}2020/1/19华中科技大学计算机学院295.4作用域作用域:程序正文中可以使用该标识符的那部分程序段。根据作用域,变量有两类:局部变量:在函数内部定义的变量,作用域是定义该变量的程序块,程序块是带有说明的复合语句(包括函数体)。不同函数可同名,同一函数内不同程序块可同名。形式参数是局部变量。外部变量:在函数外部定义的变量,其作用域从其定义处开始一直到其所在文