第5章函数与模块化设计_第1页
第5章函数与模块化设计_第2页
第5章函数与模块化设计_第3页
第5章函数与模块化设计_第4页
第5章函数与模块化设计_第5页
已阅读5页,还剩49页未读 继续免费阅读

下载本文档

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

文档简介

第5章函数与模块化设计学习目的与要求:掌握函数的定义及调用方法理解并掌握参数的传递方法理解函数的嵌套与递归调用理解并掌握全局变量与局部变量,并掌握函数中全局变量与局部变量的使用方法了解变量的存储方式

函数是程序设计语言的重要里程碑之一,它标志着程序模块化设计和软件重用的开始。简单地说,模块化设计就是把一个复杂的问题按照功能划分为若干简单的功能模块,以模块为单位进行程序设计。模块化的目的是为了降低程序复杂度,使程序设计、调试和维护等操作简单化。一般地,将复杂问题划分为不同的模块以后更适合软件的集体开发,不同的模块由不同的程序员设计,只需要确定好模块之间的接口关系,就可以实现模块之间的相互调用,而模块内部的具体实现可以由程序员自己设计。在C语言中,模块是通过函数来实现的。基本内容参数的传递概述函数的定义与调用本章小结全局变量与局部变量函数的嵌套与递归调用变量的存储方式概述#include<stdio.h>intmain(){

printf(“thisisthefirstprogram.\n”); return1;}

标准的库函数并不能满足用户所有的需求,用户还可以根据程序的需要自定义函数,通过对函数的调用来完成相应的功能。概述例5-1编写程序,计算。intm,n,i,fac1,fac2,fac3;printf("请输入两个整数n和m(n>m):");scanf("%d%d",&n,&m);fac1=fac2=fac3=1;for(i=1;i<=m;i++) fac1=fac1*i;for(i=1;i<=n;i++) fac2=fac2*i;for(i=1;i<=n-m;i++) fac3=fac3*i;printf("计算结果为:%d\n",fac2/(fac1*fac3));概述

这三次循环中,除了循环的次数(即终值)不同以外,其它的代码完全一样,为减少代码的重写,可以把这部分通用代码从程序中抽出来,利用函数来实现。intfac(intn){ inti,result=1; for(i=1;i<=n;i++) result=result*i; returnresult;}intm,n;printf("请输入两个整数n和m(n>m):");scanf("%d%d",&n,&m);printf("计算结果为:%d\n",fac(n)/(fac(m)*fac(n-m)));概述

(1)从用户使用的角度看,函数包括两种:由系统提供的、无需用户定义的标准函数,这些函数根据不同的功能存放在不同的头文件中。用户根据需要编写具有特定功能的自定义函数。

(2)C语言中所有的函数都是平行的,它们之间都是互相独立的。函数只能嵌套调用,不能嵌套定义。

(3)一个源程序文件中,可以由一个或多个函数以及其它有关内容(如数据声明与定义)组成,但必有一个main函数。程序总是从main函数开始和结束的。基本内容参数的传递概述函数的定义与调用本章小结全局变量与局部变量函数的嵌套与递归调用变量的存储方式函数的定义intfac(intn){ inti,result=1; for(i=1;i<=n;i++) result=result*i; returnresult;}数据类型函数名(数据类型形式参数1,…,数据类型形式参数n){

函数体}函数的定义例5-2编写函数,判断某一年是否为闰年。要求如果是闰年,函数返回1,不是返回0。解题思路:判断某一年是否闰年,只要判断该年是否能被400整除或能被4整除并且不能被100整除即可。将年作为函数的形式参数,在函数中对其进行判断。由于函数要求返回的值是1或者0,可以将函数的返回值定义为整型。intjudge_year(intyear){ if((year%4==0&&year%100!=0)||(year%400==0)) return1; else return0;}函数的定义

(1)在对函数的形式参数进行定义时,需要声明每一个变量的类型,不能像通常的变量声明那样,使用变量列表。

intmax(inta,intb) //正确的参数定义

intmax(inta,b) //错误的参数定义(2)函数的返回类型原则上应与函数体内return语句中表达式的类型保持一致,如果二者出现不一致的情况,以函数的返回类型为准。当数据的类型是数值型时,系统会进行自动类型转换。函数的定义例5-3

编写函数,计算两个数的最大值。intimax(floata,floatb){ floatc; c=(a>b)?a:b; returnc;}voidmain(){ floatx,y; intz; printf("请输入两个实数:"); scanf("%f%f",&x,&y); z=imax(x,y); printf("两个数的最大值是:%d\n",z);}函数的调用函数名(实际参数1,实际参数2,…,实际参数n);(1)定义函数时使用的参数称作形式参数,调用函数时使用的参数称作实际参数。形式参数必须是变量,而实际参数可以是常量、变量、表达式或函数,但必须有确定的值。例如:

c=max(9,5); //实参是常量

c=max(x,y); //实参是变量

c=max(x+2,y*3); //实参是表达式

c=max(max(x,y),z);//求三个数的最大值,实参是函数(2)传递的实际参数与函数定义的形式参数顺序和类型应保持一致;(3)执行函数调用时,形式参数和实际参数是两个不同的存储单元。(4)在一个函数中可以有多个return语句,但只要执行到其中一个return语句,就结束该函数的调用。函数的调用例5-4

输入一个整数,判断该整数是否满足如下条件:它加上100后是一个完全平方数,再加上168又是一个完全平方数。请编写函数实现。解题思路:判断一个数是否是完全平方数,只需要判断该数开平方后得到的整数平方是否与该数相等,如果相等,这个数是完全平方数,如果不相等,这个数不是完全平方数。给函数传递一个整数,判断该数加上100后是否是一个完全平方数,若是,然后再加上168,判断新数是否还是一个完全平方数,条件满足返回值为1,否则返回值为0。intjudge(intn){ intx,y; x=(int)sqrt(n+100); y=(int)sqrt(n+268); if(x*x==n+100&&y*y==n+268) return1; else return0;}函数的声明C语言中的被调函数或者库函数,或者是用户自定义的函数。(1)如果是库函数,则需要引入相应的头文件;(2)如果是用户自定义的函数,有两种情况:①被调函数在主调函数之前进行定义,则不用进行函数声明,主调函数可以直接调用被调函数。②被调用在主调函数之后进行定义,则在主调函数调用之前对被调函数进行函数声明。函数类型函数名(形参类型1形参1,形参类型2形参2,……);函数类型函数名(形参类型1,形参类型2,……);函数的声明例5-5

编写程序,计算一个整数各位数字之和,例如123,各位数字之和为1+2+3=6。解题思路:首先编写求各位数字之和的sum函数,它的类型为int型,它有一个参数number,通过循环计算出number中每位数字的和result,其类型也应为int型。若sum函数在main函数之后进行定义,在调用函数之前,要对sum函数进行声明。#include<stdio.h>intsum(intnumber); //函数声明voidmain(){intnumber,result;scanf("%d",&number);result=sum(number);printf("整数%d的各位数字之和是%d\n",number,result);}intsum(intnumber){ //函数定义

intresult=0;while(number>0){ result=result+number%10; number=number/10;}returnresult;}基本内容参数的传递概述函数的定义与调用本章小结全局变量与局部变量函数的嵌套与递归调用变量的存储方式参数的传递——普通变量作为函数参数

在C语言对函数进行调用时,将主调函数的实参传给被调函数的形参,同时编译系统为形式参数分配内存单元。在函数调用结束后,编译系统会立即释放形参所占用的内存单元。这里有两点需要强调:(1)函数中数据传递是单向的,只能把实参传给形参;(2)函数中的形参只在函数内部有效,在函数调用过程中,形参值的改变不会影响实参。一旦函数调用结束,形参变量所占的内存空间被释放。参数的传递——普通变量作为函数参数例5-6

输入两个整数,编写函数实现两个整数值的交换。解题思路:首先编写数值交换的swap函数,它有两个参数,是两个简单变量,由于函数的功能是实现两个数的交换,无需返回值,所以它的函数类型为void型,若swap函数在main函数之后进行定义,在调用函数之前,要对swap函数进行声明。#include<stdio.h>intmain(){voidswap(int,int);inta,b;printf("请输入两个整数:");scanf("%d%d",&a,&b);printf("交换前:a=%d,b=%d\n",a,b);swap(a,b);printf("交换后:a=%d,b=%d\n",a,b);return0;}voidswap(intx,inty){inttemp;printf(“函数swap中,交换前:x=%d,y=%d\n",x,y);temp=x;x=y;y=temp;printf("函数swap中,交换后:x=%d,y=%d\n",x,y);}参数的传递——数组元素作为函数参数

数组元素只能作为实参,不能作为形参。这是因为形参是在函数被调用时才分配存储空间,而数组中的元素是在定义数组时系统已经分配了存储空间,因而编译系统在函数调用时无法为某一个数组元素单独分配存储空间。而将数组元素作为实参传递给函数的形参时,只需将数组元素看成是一个具体的变量传递给函数的形参,形参的相关运算并不能改变实参的值。

C语言中,函数参数的传递有两种:值传递和地址传递。数组元素作函数参数实现的是值传递,数组名作函数参数实现的是地址传递。参数的传递——数组元素作为函数参数例5-7

给定一个长度为10的数组,要求输出该数组中的最大数及其在该数组中的位置。解题思路:若求数组中的最大值和最大值在数组中的位置,假设第一个元素为最大元素,编写求两个数的最大值函数max,该函数包含两个参数,其中一个参数是存储最大值的变量,一个参数是数组元素,然后扫描整个数组,不断更新最大元素以及其所在的位置,最终找出该数组中最大元素及其所在的位置。intmax(intx,inty); inta[10]={10,100,98,789,98,0,121,981,78,191};inti,maxNumber,index;maxNumber=a[0];index=0;for(i=1;i<10;i++)if(max(maxNumber,a[i])>maxNumber){ maxNumber=a[i]; index=i;}printf("数组中的最大值是%d,是数组中第%d个元素\n",maxNumber,index+1);intmax(intx,inty){ returnx>y?x:y;}参数的传递——数组名作为函数参数

在C语言中的函数中,除了数组元素可以作为参数外,数组名也可以作为参数。与数组元素作为参数不同的是,数组元素作为参数时是将数组元素的值传递给函数的形参,而数组名作为参数时,向形参传递的是数组首元素的地址。参数的传递——数组名作为函数参数例5-9

用选择法对数组中的N个整数按从小到大的顺序进行排序。解题思路:所谓的选择排序法就是:取待排序序列中的第1个元素,依次与序列中的其它元素进行比较,找出最小值与该值在数组中的位置,将最小值与第1个元素进行交换。然后取第2个元素,依次与序列中从第3个元素开始的元素进行比较,找出次小值与第2个元素进行交换。重复执行上述操作,直到所有元素都排好序为止。voidsort(inta[],intn){

inti,j,k,temp;

for(i=0;i<n-1;i++){

k=i;for(j=i+1;j<n;j++)

if(a[k]>a[j]) k=j; if(i!=k){ temp=a[i]; a[i]=a[k]; a[k]=temp; }}}参数的传递——数组名作为函数参数(1)用数组名作为函数参数,需要在主调函数和被调函数中分别定义数组,不能只在一方定义。如上例中的数组a是形参数组名,它与主调函数中的实参数组名score相对应。(2)在C语言的编译系统中,并不检查形参中数组的大小,只是将实参数组的首地址传给形参,编译系统不为形参分配新的存储空间,两个数组共同占有相同的存储单元。(3)由于编译系统中并不检查形参中数组的大小,因此可以在形参中数组名后加一个空括号即可。如上例中的sort函数的定义方式:voidsort(inta[],intn);(4)实参数组应与形参数组类型一致。基本内容参数的传递概述函数的定义与调用本章小结全局变量与局部变量函数的嵌套与递归调用变量的存储方式函数的嵌套与递归调用

在C语言中,所有的函数是互相平行、相互独立的。不能在一个函数中定义另一个函数,也就是说函数不能嵌套定义。但C语言允许函数之间互相调用,同时允许被调用的函数中再调用其它的函数。在C语言中,允许被调用的函数进一步调用其它函数,称为函数的嵌套调用。在函数调用中,若一个函数直接或间接地调用自身,称作函数的递归调用。函数的嵌套调用例5-12

编写程序,从输入的4个整数找出其中的最大数。解题思路:编写求两个数的最大值函数max2,再编写求4个数的最大值函数max4,在主调函数传递4个整数给max4中的形参。在max4函数中首先将4个数分为两组,找出两组数中的最大值后,再找出这两个最大值的最大值。intmax4(inta,intb,intc,intd){ inttemp1,temp2,temp; temp1=max2(a,b); temp2=max2(c,d);

temp=max2(temp1,temp2); returntemp;}intmax2(inta,intb){

returna>b?a:b;}函数的嵌套调用intmain(){inta,b,c,d;intmaxValue;printf("请输入4个整数:\n");scanf("%d%d%d%d",&a,&b,&c,&d);maxValue=max4(a,b,c,d);printf("4个数的最大值是:%d\n",maxValue);return0;}函数的递归调用

一个递归的问题可以分为“回溯”和“递推”两个阶段。一般地,递归函数的定义需要解决两点:(1)函数的结束条件;(2)函数的递归方式。函数的结束条件表示递归不能无限制运行下去,必须在有限步内执行结束。而函数的递归方式则表明规模较大的问题解与规模较小的问题之间的关系,通过规模较小的问题解逐渐得到规模较大的问题解。函数的递归调用例5-14

编程用递归方法计算阶乘函数。#include<stdio.h>intfac(intn);intmain(){ intn,result; printf("请输入整数n:"); scanf("%d",&n);

result=fac(n); printf("%d!=%d\n",n,result); return0;}intfac(intn){ if(n==1) return1; else

returnn*fac(n-1);}

函数的递归调用intfac(intn){ if(n==1) return1; else

returnn*fac(n-1);}

函数的递归调用例5-15Hanoi塔问题。相传印度教的天神在创造世界时,建了一座神庙,庙里竖立了3根柱子。天神将64个直径大小不一的金盘子,按照从大到小的顺序依次套放在第一根柱子上,形成了一座Hanoi塔。天神让庙里的僧侣们将第一根柱子上的盘子借助第2根柱子全部移到第3根柱子上。同时规定:每次只能移动一个盘子;盘子只能在3根柱子上来回移动而不能放在他处;在移动过程中,3根柱子上的盘子必须始终保持大盘在下,小盘在上。天神说当这64个盘子全部移到第三根柱子上后,世界末日就要到了。试编程实现Hanoi塔上盘子的移动步骤。函数的递归调用解题思路:该问题没有明显的递推规律,因此解决问题关键是找出递归函数的定义方式。如果只有一个盘子,只需要将该盘子从A移动到C即可。如果有多于一个盘子,最终的目标是将这个盘子从A移动到C,并且仍然按照原来的大小顺序。因此需要将A柱上最大的盘子移动到C柱上最下面,这就需要将上面的盘子移动到B柱上。即移动需要分为三步:(1)将上面的个盘子借助C柱移动到B柱,将最大的盘子从A柱移动到C柱,将B柱上的个盘子借助A柱移动到C柱。函数的递归调用#include<stdio.h>voidhanoi(intn,chara,charb,charc);intmain(){intn;printf("请输入要移动的盘子数:");scanf("%d",&n);hanoi(n,'A','B','C');return0;}voidhanoi(intn,chara,charb,charc){if(n==1)printf("%c---->%c\n",a,c);

else{

hanoi(n-1,a,c,b);printf("%c---->%c\n",a,c);hanoi(n-1,b,a,c);}}基本内容参数的传递概述函数的定义与调用本章小结全局变量与局部变量函数的嵌套与递归调用变量的存储方式全局变量与局部变量

在不同位置定义的变量,其作用域是不同的。例如,有的变量是在函数内部定义的,当函数被调用时,该变量被分配存储空间,而当函数调用结束后,该变量所占的存储空间被编译系统收回,此时再引用该变量就会出现编译错误。因此,C语言中的变量有着不同的作用域和生存周期。根据变量的作用域,可以将变量分为全局变量和局部变量;根据变量的生存周期,可以将变量分为静态存储方式与动态存储方式。全局变量与局部变量

所谓变量的作用域,指的是变量的空间有效性。如果一个变量是在函数内部定义的,则该变量是局部变量。如果变量是在函数外部定义的,称该变量为全局变量。全局变量与局部变量的主要区别有:(1)全局变量在程序开始时分配存储空间,在程序结束时释放;局部变量在函数被调用时分配存储空间,在函数调用结束时释放存储空间。(2)局部变量只在定义该变量的函数内部有效,不能在函数外部引用该变量;而全局变量在定义后可以在程序的所有地方引用。(3)对局部变量而言,只能在定义该变量的函数内部对其进行修改;而全局变量在定义以后,程序的任何地方都可以修改。(4)若不给变量初始化,全局变量的初值为0(数值型数据),局部变量的初值为任意值。全局变量与局部变量全局变量与局部变量例5-18

写出下列程序的运行结果。基本内容参数的传递概述函数的定义与调用本章小结全局变量与局部变量函数的嵌套与递归调用变量的存储方式变量的存储方式

根据变量的时间有效性,变量可以分为静态存储方式和动态存储方式。静态存储方式,是指在程序运行期间由系统分配固定的存储空间的方式,而动态存储方式,是指在程序运行期间根据需要进行动态的分配存储空间的方式。当一个C语言程序编译完成后,内存中将会为编译好的程序分配一部分存储空间。一般地,这部分存储空间可以分为三部分:(1)程序区:存放编译好的程序代码;(2)静态存储区:用来存储全局变量,当程序开始运行时为全局变量分配存储空间,程序运行结束时释放;(3)动态存储区:用来存放局部变量、函数调用的返回地址等。当函数调用结束后,这些存储空间将被自动释放。变量的存储方式——自动变量

自动变量是C语言中变量和函数默认的存储类别,它的特点是:在函数调用时为其分配存储空间,在调用完成后自动释放这些存储空间。

auto数据类型变量名;#include<stdio.h> intmain() { inta,b; autointc; printf("请输入两个整数:"); scanf("%d%d",&a,&b); c=a+b;

printf("%d+%d=%d\n",a,b,c);

return0;}inta;autointa;变量的存储方式——静态(static)变量

静态变量与局部变量的区别在于当函数调用结束后,动态变量所占的存储空间被释放,而静态变量所占的存储空间不被释放,因而局部变量的值得以保留,在下一次调用该函数时,该局部变量的初始值为上一次调用结束时的值。static数据类型变量名;变量的存储方式——静态(static)变量

#include<stdio.h>intmain(){ intf(int); inti; for(i=0;i<2;i++) printf("result=%d\n",f(i)); return0;}intf(inta){ staticintb=3; b++; returna+b;}变量的存储方式——静态(static)变量例5-20

编程计算从整数1到输入的整数m的阶乘。解题思路:由于1!=1,2!=2*1!,…,m!=m*(m-1)!,所以定义fac函数。函数中定义静态局部变量f,并赋初值为1,表示求得的某个数的阶乘。多次调用该函数,每调用一次,f保留上一次得到的值,而参数的值是从1到m。#include<stdio.h>intmain(){intfac(int); inti,m,result;printf("请输入一个整数:");scanf("%d",&m);for(i=1;i<=m;i++)printf("%d!=%d\n",i,fac(i));return0;}intfac(intn){ staticintf=1; f=f*n;returnf;}变量的存储方式——静态(static)变量

(1)静态变量存储在静态存储区,每次函数调用后不释放所占用的存储空间,因此静态变量的值得以保留;而自动变量存储在动态存储区,每次函数调用后需要释放所占用的存储空间;(2)静态存储变量只进行一次初始化操作,在后续的调用过程中将不再进行静态变量的初始化工作;而在函数的每一次调用时都需要对自动变量进行初始化。如果静态变量不进行初始化,其初始值为0或者为空字符,而自动变量如果不进行初始化,它的值是不确定的;(3)静态变量从本质上仍然属于局部变量,因此除了定义它的函数可以引用它以外,其它函数并不能引用它或者对它的值进行修改;(4)由于静态变量需要一直占用程序的存储空间,同时会降低程序的可读性,因此在编写程序中,不提倡使用静态变量。变量的存储方式——寄存器(register)变量

在计算机运行程序时,所有的变量都要放到CPU内部的寄存器中。由于CPU内部的寄存器数量是有限的,因此需要频繁地在内存和寄存器之间进行数据的存取。考虑到寄存器中数据的操作速度远高于内存中数据的操作速度,因此经常把使用频繁的局部变量声明为寄存器变量,将其存储在CPU的寄存器中,这样可以有效提高程序的运行效率。这样的变量称为寄存器变量。register数据类型变量名;变量的存储方式——寄存器(register)变量例5-21

求n!,当n的值很大时,可以把循环变量作为寄存器变

温馨提示

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

最新文档

评论

0/150

提交评论