版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
主讲:信息科学与工程学院计算机系
卡米力C程序设计(第四版)
学好程序设计语言的唯一途径是上机练习。你的编程能力与你在计算机上投入的时间成正比。
课程要求♣课前请做好预习♣保持课堂安静,头脑清醒,思维活跃♣做好课堂笔记♣认真、独立、按时完成并提交作业♣重视上机实践,有效利用宝贵的上机时间§7.1
为什么要用函数§7.2
怎样定义函数§7.3
调用函数§7.4
对被调用函数的声明和函数原型§7.5
函数的嵌套调用§7.6
函数的递归调用§7.7
数组作为函数参数§7.8
局部变量和全局变量§7.9
变量的存储方式和生存期§7.10
关于变量的声明和定义§7.11
内部函数和外部函数
第7章函数§7.1为什么要用函数
问题:如果程序的功能比较多,规模比较大,把所有代码都写在main函数中,就会使主函数变得庞杂、头绪不清,阅读和维护变得困难。有时程序中要多次实现某一功能,就需要多次重复编写实现此功能的程序代码,这使程序冗长,不精炼。
解决的方法:用模块化程序设计的思路采用“组装”的办法简化程序设计的过程事先编好一批实现各种不同功能的函数把它们保存在函数库中,需要时直接用.函数就是功能:函数就是用来完成特定的功能的代码函数名就是给该(函数)功能起的名字函数的名字应反映其代表的功能函数名应该遵循“见名知意”。在设计一个较大的程序时,往往把它分为若干个程序模块,每一个模块包括一个或多个函数,每个函数实现一个特定的功能。C程序可由一个主函数和若干个其他函数构成;主函数调用其他函数,其他函数也可以互相调用;同一个函数可以被一个或多个函数调用任意多次。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。在程序开发中,常将一些常用的功能模块编写成函数,放在公共函数库中供大家选用。函数间的调用关系:图7-1函数之间的调用关系示意图例7.1输出一行信息,用函数调用来实现#include<stdio.h>voidmain(){voidprintstar();
/*函数printstar的声明*/
voidprint_message();
/*函数print_message的声明*/
printstar();
/*调用自定义函数printstar*/
print_message();
/*调用自定义函数print_message*/
printstar();
/*调用自定义函数printstar*/}voidprintstar()/*printstar函数的定义*/{
printf("*************\n");}voidprint_message(){
printf("Howdoyoudo!\n");}解题思路:在输出的文字上下分别有一行“*”号,显然不必重复写这段代码,用一个函数print_star来实现输出一行“*”号的功能。再写一个print_message函数来输出中间一行文字信息用主函数分别调用这两个函数。#include<stdio.h>intmain(){void
print_star();
voidprint_message();
print_star();
print_message();
print_star();
return0;}voidprint_star(){printf(“******************\n”);}voidprint_message(){printf(“Howdoyoudo!\n”);}声明函数定义函数函数的调用
说明:(1)一个C程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对较大的程序,一般不希望把所有内容全放在一个文件中,而是将他们分别放在若干个源文件中,再由若干源程序文件组成一个C程序。这样便于分别编写、分别编译,提高调试效率。一个源程序文件可以为多个C程序公用。(2)一个源程序文件由一个或多个函数以及其他有关内容(如命令行、数据定义等)组成。一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的。(3)C程序的执行是从main函数开始的,如是在main函数中调用其他函数,在调用后流程返回到main函数,在main函数中结束整个程序的运行。(4)所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另一函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用main函数。main函数是系统调用的。(5)从用户使用的角度看,函数有两种:①标准函数,即库函数。这是由系统提供的,用户不必自己定义这些函数,可以直接使用它们。应该说明,不同的C系统提供的库函数的数量和功能会有一些不同。②用户自己定义的函数。用以解决用户的专门需要。(6)从函数的形式看,函数分两类:
①无参函数。如例7.1中的printstar和print_message就是无参函数。在调用无参函数时,主调函数不向被调用函数传递数据。无参函数一般用来执行指定的一组操作。voidprint_message()/*定义print_message函数*/{
printf("Howdoyoudo!\n");}②有参函数。在调用函数时,主调函数在调用被调用函数时,通过参数向被调用函数传递数据,一般情况下,执行被调用函数时会得到一个函数值,供主调函数使用。例如:intmax(intx,inty)
{intz;
/*函数体中的声明部分*/
z=x>y?x:y;/*函数体中的执行语句*/
return(z);
}形式参数§7.2怎样定义函数
§7.2.1.为什么要定义函数
C语言要求,在程序中用到的所有函数,必须“先定义,后使用”。例如,想调用max函数来求出两个数中的大者,必须事先定义要调用的max函数,即指定函数名字、函数返回值类型、函数实现的功能以及参数的个数与类型,将这些信息通知编译系统。定义函数应包括以下几个内容:指定函数的名字,以便以后按名调用;指定函数类型,即函数返回值的类型;指定函数参数的名字和类型,以便在调用函数时向它们传递数据;指定函数的功能。这是最重要的,这是在函数体中解决的。对于库函数,程序设计者只需用#include指令把有关的头文件包含到本文件模块中即可.在有关的头文件中包括了对函数的声明,例如,在程序中若用到数学函数(sqrt,fabs等),则在源文件的开头写上:
#include<math.h>当然,程序设计者需要在程序中自己定义想用的而库函数并没有提供的函数§7.2.2定义函数的方法
1.定义无参函数在定义函数时要用“类型名”指定函数值的类型,即函数带回来的值的类型。例7.1中的printstar和print_message函数都是无参函数,函数名后面的括号中是空的(void
),没有任何参数。定义无参函数的格式:定义无参函数的一般形式为:类型名
函数名(void){
函数体
}
类型名
函数名(){
函数体
}
包括声明部分和语句部分包括声明部分和语句部分例无参函数
void
printstar(){printf(“**********\n”);}或
void
printstar(void){printf(“**********\n”);}定义函数时要用“类型名”指定函数值的类型,即函数带回来的值的类型.函数名后的void表示空,即函数无参数
2.定义有参函数定义有参函数的一般形式为:返回值类型
函数名(形式参数表列){
声明部分语句部分
}函数体例如:intmax(intx,inty){intz;
//声明部分
z=x>y?x:y;//执行部分
return(z);
}花括号内是函数体,它包括声明部分和语句部分。在声明部分定义所用的变量,此外对将要调用的函数作声明。在函数体的语句中求出z的值(为x与y中大者);return(z)的作用是将z的值作为函数值带回到主调函数中。return后面的括弧中的值(z)作为函数带回的值(或称函数返回值)。在函数定义时已指定max函数为整型,在函数体中定义z为整型,二者是一致的,将z作为函数max的值带回调用函数。如果在定义函数时不指定函数类型,系统会隐含指定函数类型为int型。因此上面定义的max函数左端的int可以省写.这是一个求x和y二者中大者的函数,笫1行第一个关键字int表示函数值是整的。max为函数名。括号中有两个形式参数x和y,它们都是整型的。在调用此函数时,主调函数把实际参数的值传递给被调用函数中的形式参数x和y。
3.定义空函数
调用此函数时,什么工作也不做,没有任何实际作用。在主调函数中写上“dummy();”表明“这里要调用一个函数”,而现在这个函数没有起作用,等以后扩充函数功能时补充上。
例如:voiddummy(){}定义空函数的一般形式为:返回值类型函数名()
{
//函数体为空
}……在程序设计中往往根据需要确定若干模块,
分别由一些函数来实现。
而在第一阶段只设计最基本的模块,其他一些次要功能或锦上添花的功能则在以后需要时陆续补上。在编写程序的开始阶段,可以在将来准备扩充功能的地方写上一个空函数(函数名取将来采用的实际函数名(如用merge()、matproduct()、concatenate()、shell()等,分别代表合并、矩阵相乘、字符串连接、希尔法排序等),只是这些函数未编好,先占一个位置,以后用一个编好的函数代替它。这样做,程序的结构清楚,可读性好,以后扩充新功能方便,对程序结构影响不大。空函数在程序设计中常常是有用的。
§7.3函数调用定义函数的目的就是为了调用此函数,以得到预期的结果。§7.3.1函数调用的形式调用一个函数的方法很简单,如:
print_star();//调用无参函数
c=max(a,b);//调用有参函数函数调用形式:如果是调用无参函数,则“实参表列”可以没有,但括号不能省略;如果实参表列包含多个实参,则各参数间用逗号隔开。函数名(实参表列);按函数调用在程序中出现的形式和位置来分,可以有以下3种函数调用方式:1.函数调用语句把函数调用单独作为一个语句。例如:
printf_star();这时不要求函数带回值,只要求函数完成一定的操作。函数名(实参表列);2.函数表达式函数调用出现在另一个表达式中。例如:c=max(a,b);。c=max(a,b);
是一次函数调用,它是赋值表达式中的一部分。这时要求函数带回一个确定的值以参加表达式的运算。变量=函数名(实参表列);3.函数参数函数调用作为另一函数调用时的实参。例如:m=max(a,max(b,c));其中max(b,c)是一次函数调用,它的值作为max另一次调用的实参。经过赋值后,m的值是a,b,c三者中的最大者,又如:printf(“%d”,max(a,b));也是把max(a,b)作为printf函数的一个参数。
§7.3.2函数调用时的数据传递1.形式参数和实际参数在调用函数时,大多数情况下,主调函数和被调用函数之间有数据传递关系。
这就是前面提到的有参函数。♣
在定义函数时函数名后面括号中的变量名
称为“形式参数”(简称“形参”);♣
在主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)称为
“实际参数”(简称“实参”)。
♣实参可以是常量、变量或表达式。
在不同的函数之间传递数据时,可以使用的法:♣
参数:通过形参和实参传递数据;♣
返回值:用return语句返回计算结果;♣
全局变量:外部变量。函数参数及其传递方式:形参与实参:♣
实际参数:调用函数时函数名后面括号中的变量名。♣
形式参数:定义函数时函数名后面
括号中的变量名。♣
函数参数传递方式:函数调用时将
实参值传递给形参。2.实参和形参间的数据传递在调用函数过程中,系统会把实参的值传递给被调用函数的形参,或者说,形参从实参得到一个值。该值在函数调用期间有效,可以参加被调函数中的运算。在调用函数过程中发生的实参与形参间的数据传递,称为虚实结合。例7.2
输入两个整数,要求输出其中值较大者。要求用函数来找到大数。解题思路:1)函数名应是见名知意,今定名为max;2)由于给定的两个数是整数,返回主调函数
的值(即较大数)应该是整型。3)max函数应当有两个参数,以便从主函数接收两个整数,因此参数的类型应当是整型先编写max函数:int
max(int
x,inty)//函数的定义,x和y是形参{intz;z=x>y?x:y;
return(z);}
在max函数上面,再编写主函数#include<stdio.h>intmain(){int
max(int
x,inty);//函数的声明
int
a,b,c;
printf(“twointegernumbers:");
scanf(“%d,%d”,&a,&b);c=max(a,b);
//函数的调用,实参可以是常量变量或表达式
printf(“maxis%d\n”,c);}#include<stdio.h>intmain(){int
max(int
x,inty);//函数的声明
int
a,b,c;
printf(“twointegernumbers:");
scanf(“%d,%d”,&a,&b);c=max(a,b);
//函数的调用,实参可以是常量变量或表达式
printf(“maxis%d\n”,c);}int
max(int
x,inty)//函数的定义,x和y是形参{intz;z=x>y?x:y;
return(z);}在调用此函数时,主调函数把实参的值传递给max函数中的形参x和y,实现了参数值的传递,把二者组合为一个程序文件,主函数在前面,max函数在下面。如:文件名为ka1.c说明:在笫2行的int表示函数值是整的。主函数中给出了被调用函数max的声明,max为函数名。括号中有两个int型的形式参数x和y。用c=max(a,b);调用了max函数这时在max函数中得到了要处理的两个数据,得到的最大值返回给c注意:1)实参可以是常量、变量或表达式,如:max(3,a+b),但要求它们有确定的值;在函数调用时将实参的值传递给形参。2)实参与形参的类型应相同或赋值兼容。例7.2中实参和形参的类型相同,都是int
型,这是合法的;如果实参为int型而形参x为float型,或者相反,则按不同类型数值的赋值规则进行转换.例如,实参a为float型变量,其值为3.5,而形参x为int型,则在传递时先将实数3.5转换成3,然后送到形参x。char型和int型通用.main(){int
a,b,c;
scanf("%d,%d",&a,&b);c=max(a,b);
printf("Maxis%d",c);}max(int
x,inty)//函数定义{intz;z=x>y?x:y;
return(z);}形参实参(max函数)c=max(a
,b);(main函数)max(intx,inty
){intz;z=x>y?x:y;
return(z);}
实参和形参间的数据传递voidmain(){intx,y,z;intmax(int
a,intb);printf("请输入两个整数:\n");scanf(“%d%d”,&x,&y);z=max(x,y);
printf(“max=%d\n”,z);}intmax(inta,intb){intc;c=a>b?a:b;returnc;}
实参与形参,参数传递:x3y5zabc5355
求两个数中的最大数:①在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数max中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。
§7.3.3函数调用的过程2a3bxy23实参形参②调用函数时将实参对应的值传递给形参,如图7.3所示,实参的值为2,把2传递给相应的形参x,这时形参x就得到值2,同理,形参y得到值3。#include<stdio.h>intmain(){int
max(int
x,inty);
//函数的声明
int
a,b,c;
printf(“twointegernumbers:");
scanf(“%d,%d”,&a,&b);c=max(a,b);
//函数的调用
printf(“maxis%d\n”,c);}int
max(int
x,inty)//函数的定义{intz;z=x>y?x:y;
return(z);}③在执行max函数期间,由于形参已经有值,就可以利用形参进行有关的运算(例如,x和y比较,把最大值赋给z等)。#include<stdio.h>intmain(){int
max(int
x,inty);
//函数的声明
int
a,b,c;
printf(“twointegernumbers:");
scanf(“%d,%d”,&a,&b);c=max(a,b);
//函数的调用
printf(“maxis%d\n”,c);}int
max(int
x,inty)//函数的定义{intz;z=x>y?x:y;
return(z);}④通过return语句将函数值带回到主函数。例7.2中在return语句中指定的返回值是z,这个z就是函数max的返回值。执行return语句就把这个函数返回值带回主函数main。注意:返回值z的类型与max函数的类型一致。若函数不需要返回值,则不需要return语句,这时函数类型应定义为main(){int
max(int
x,inty);
//函数的声明
int
a,b,c;scanf(“%d,%d”,&a,&b);c=max(a,b);
//函数的调用
printf(“maxis%d\n”,c);}int
max(int
x,inty)//函数的定义{intz;z=x>y?x:y;
return(z);}⑤函数调用结束,则形参单元被释放。注意:实参单元仍保留并维持原值,实参的值并没有改变。如果在执行一个被调用函数时,若形参的值发生改变,则不会改变主调函数的实参的值.例如:形参在执行max函数的过程中,形参x,y的值变为10和15,但a和b的值仍为2和3,这是因为实参与形参在内存中占用不同的存储单元。2a3bxy1015实参形参//例子函数参数为普通变量时形参对实参的影响
voidmain(){int
swap(int
x,inty);//被调用函数swap的声明
int
a,b,result;
printf("请输入需要交换的两个整数:\n");
scanf("a=%d,b=%d",&a,&b);
printf("交换之前的值是:a=%d,b=%d\n",a,b);
swap(a,b);//swap函数的调用printf("返回到main函数后的最终交换结果是:a=%d,b=%d\n",a,b); }
int
swap(int
x,inty)//swap函数的定义
{inttemp;temp=x;x=y;y=temp;printf("在swap函数中两个数交换的结果是:a=%d,b=%d\n",x,y);}在C语言中,实参向形参的数据传递是“值传递”,单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。c=max(a
,b);
(main函数)max(intx,inty){intz;z=x>y?x:y;
return(z);}
(max函数)2a3bxy1015实参形参【例】编写求任意数之幂的程序即求xn。分析:在前面已给出了求x的n次方的函数power(),所以只要在主函数中,输入x和n的值,并作为power()的参数,加上调用power(x,n)语句即可,完整的程序清单如下:#include<stdio.h>voidpower(float
x,intn);/*函数说明*/voidmain(){floatx;
intn;
printf(“输入x=?n=?\n”);
scanf(“%f%d”,&x,&n);
power(x,n);
/*函数调用*/ return0;}
voidpower(floatx,intn)/*函数定义*/{inti;doublep=1.0;for(i=1,p=1;i<=n;i++)p*=x;/*xn=x*x*x…..*x即n次连乘*/
printf(“%f的%d次方=%f\n”,x,n,p);}§7.3.4函数的返回值通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。例如:例7.2中,c=max(2,3);的值是3;c=max(5,2);的值是5。赋值语句将这个函数值3或5赋给变量c。例如:
c=max(5,2);main()
{
intmax(intx,inty);
inta,b,c;
scanf("%d,%d",&a,&b);
c=max(a,b);
/*函数的调用*/
printf("Maxis%d",c);
}max(intx,inty)
/*定义有参函数max*/
{
intzz=x>y?x∶y;
return(z);
}①②③②④return语句将被调用函数中的一个确定值带回主调函数中去。见图8.2中从return语句返回的箭头。关于函数返回值的一些说明:
①函数的返回值是通过函数中的return语句获得的。
return语句将被调用函数中的一个确定值带回主调函数中去。见图7.2中从return语句返回的箭头。如果需要从被调用函数带回一个函数值供主调函数使用,被调用函数中必须包含return语句。如果不需要从被调用函数带回函数值可以不要return语句。一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个语句起作用。return语句后面的括弧也可以不要,如:“returnz;”
等价于“return(z);”max(intx,inty){
return(x>y?x∶y);}return后面的值可以是一个表达式。例如,例7.2中的函数max可以改写成:这样的函数更为简短,只用一个return语句就把求值和返回都解决了。②函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。例如:下面是3个函数的首行:intmax(floatx,floaty)
/*函数值为整型*/charletter(charc1,charc2)
/*函数值为字符型*/doublemin(intx,inty)
/*函数值为双精度型*/在C语言中,凡不加类型说明的函数,自动按整型(int)处理。例7.2中的max函数首行的函数类型int可以省写,用TC2.0编译程序时能通过,但用TC++3.0编译程序时不能通过,因为C++要求所有函数都必须指定函数类型。因此,建议在定义时对所有函数都指定函数类型。③在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。
例7.3返回值类型与函数类型不同#include<stdio.h>voidmain(){int
max(float
x,floaty);floata,b;
intc;
printf("请输入2个实数:\n");
scanf("%f,%f",&a,&b);
c=max(a,b);
printf("maxis%d\n",c);}int
max(float
x,floaty){floatz;
z=x>y?x:y;
return(z);}自定义函数max定义为int型,而return语句中的z为float型,类型不一致,按上述规定,先将z转换成int型,然后max(x,y)带回一个整型值2返回主调函数main。如果将main函数中的c定义为float型,用%f格式输出,也是输出2.000000。因为调用max函数得到的是2,然后把2赋给float型的c则得到2.000000。④对于不带回值的函数,应当用“void”定义函数为“无类型”(或称“空类型”)。这样,系统就保证不使函数带回任何值,即禁止在调用函数中使用被调用函数的返回值。此时在函数体中不得出现return语句。§7.4对被调用函数的声明和函数原型
在一个函数中调用另一个函数需要具备如下条件:
被调用函数必须是已经定义的函数(是库函数或用户自己定义的函数);(2)如果使用库函数,应该在本文件开头加相应的#include指令将调用有关库函数时所需用到的信息包含到本文件中来。例如:#include<stdio.h>如果不包含<studio.h>文件中的信息,在程序中就无法使用输入输出库中的函数。若使用数学库函数中的函数,则使用:
#include<math.h>(3)如果使用自己定义的函数,而该函数的位置在调用它的函数(main函数)后面(在同一个文件中),应该在主函数中给出被调用函数的声明。声明的作用是:把函数名,函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。♣
如果使用自己定义的函数,而该函数的位置在调用它的函数(main函数)之前,则在主函数中不需要给出被调用函数的声明。函数的定义:对函数功能的确立,包括指定函数的返回值类型,函数名、形参及其类型、函数体等,它是一个完整的、独立的函数单位;函数的声明:把函数的返回值类型,函数名,函数参数后面加分号,即函数定义中的第1行(函数首部)加一个分号,就成了函数原型或函数的声明。例7.4输入两个实数,用一个函数求出它们之和。解题思路:用add函数实现。首先要定义add函数,它为float型,它应有两个参数,也应为float型。分别编写add函数和main函数,它们组成一个源程序文件。由于main函数的位置在add函数之前,因此在main函数中对add函数进行声明。注意:在main函数中要给出add函数的声明#include<stdio.h>intmain(){floatadd(floatx,floaty);floata,b,c;
printf("Pleaseenteraandb:");
scanf("%f,%f",&a,&b);c=add(a,b);
printf("sumis%f\n",c);return0;}floatadd(float
x,floaty){floatz;z=x+y;
return(z);}对add函数声明:函数定义中的首行加分号由于被调用函数add的定义在main函数的下面,因此在main函数中必须给出该函数的声明。add函数的定义:包括函数的返回值类型,函数名、形参及其类型、函数体。函数的首行加分号——函数的声明。
为什么要用函数的首部来作为函数的声明?这是为了便于对函数调用的合法性进行检查.因为在函数的首部包含了检查调用函数是否合法的基本信息(它包括了函数的返回值类型、函数名、函数的参数个数、参数类型和参数顺序),在检查函数调用时要求函数名、函数类型、参数个数和参数顺序必须与函数声明一致,实参类型必须与函数声明中的形参类型相同,否则就按出错处理,这样就能保证函数的正确调用。函数原型的一般形式有两种:如:
floatadd(float
x,floaty);或者
floatadd(float,float);实际上,在函数声明中的形参名可以省写,而只写形参的类型,这是因为:编译系统只关心和检查参数个数和参数类型,而不检查参数名,因为在调用函数时只要求保证实参类型与形参类型一致,而不必考虑形参名是什么。若函数声明放在文件的开头,这时下面的所有函数都可以使用此函数。函数类型函数名(参数类型1,参数类型2……);函数类型
函数名(参数类型1,参数名1,参数类型2,参数名2……);
如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调用的函数作声明。函数原型的一般形式为:①
②floatadd(floatx,floaty);或者floatadd(float,float);在main函数之后给出了被调用函数的定义,则在main函数中调用被地调用函数之前要给出被调用函数的声明。#include<stdio.h>voidmain()
{floatadd(float,float);//给出add函数的声明floata,b,c;
printf("请输入2个实型数据:\n");
scanf("%f,%f",&a,&b);c=add(a,b);
/*函数的调用*/
printf("sumis%f\n",c);}floatadd(float
x,floaty)/*函数的定义*/
{floatz;/*函数体*/
z=x+y;
return(z);}如果在文件的开头(在所有函数之前)给出了被调用函数的声明,则在main函数中不必给出被调用函数的声明。#include<stdio.h>
intadd(int,int);
//在所有函数之前给出add函数的声明voidmain()
{int
a,b,c;
printf(“请输入2个整型数据:\n");
scanf("%d,%d",&a,&b);c=add(a,b);
/*函数的调用*/
printf("sumis%f\n",c);}
int
add(intx,inty)/*函数的定义*/
{intz;/*函数体*/
z=x+y;return(z);}
如果已在所有函数定义之前,在函数
的外部已做了函数声明,则在各个主
调函数中不必对所调用的函数再作声明。例如:charletter(char,char);//*以下3行在所有函数之前,
floatf(float,float);
//*且在函数外部*/
inti(float,float);
int
main()
{…}
/*在main()中调用这些函数时不必声明它所调用的函数*/
charletter(charc1,charc2)
/*定义letter函数*/
{…}
floatf(floatx,floaty)
/*定义f函数*/
{…}
inti(floatj,floatk)
/*定义i函数*/{…}除了以上(2)(3)(4)所提到的三种情况外,都应该按上述介绍的方法对所调用函数作声明,否则编译时就会出现错误。
§7.5函数的嵌套调用C语言的函数定义都是互相平行、独立的,也就是说在定义函数时,一个函数内不能再定义另一个函数。
C语句不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。
C语句不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。见图7.5。函数嵌套调用执行过程是:①执行main函数的开头部分;②遇函数调用a的操作语句,流程转去a函数;③执行a函数的开头部分;④遇调用b函数的操作语句,流程转去函数b;⑤执行b函数,完成b函数的全部操作;⑥返回调用b函数处,即返回a函数;⑦继续执行a函数中尚未执行的部分,直到a函数结束;⑧返回main函数中调用a函数处;⑨继续执行main函数的剩余部分直到结束。例7.5
输入4个整数,找出其中最大的数。用函数的嵌套调用来处理。解题思路:这个问题完全可以只用一个主函数就可以得到结果;现在根据题目的要求,用函数的嵌套调用来处理。main中调用max4函数,找4个数中最大者,max4中再调用max2,找两个数中的大者,max4中多次调用max2,可找4个数中的大者,然后把它作为函数值返回main函数,main函数中输出结果。#include<stdio.h>intmain(){intmax4(inta,int
b,int
c,intd);
int
a,b,c,d,max;printf(“4intergernumbers:");
scanf("%d%d%d%d",&a,&b,&c,&d);
max=max4(a,b,c,d);
printf("max=%d\n",max);return0;}
主函数对max4函数的声明:函数定义中的首行加分号调用后肯定是4个数中最大者输出最大者main中调用max4函数,找4个数中最大者,max4中再调用max2,找两个数中的大者,max4中多次调用max2,可找4个数中的大者。intmax4(inta,int
b,int
c,intd)//max4函数的定义{intmax2(inta,intb);
intm;m=max2(a,b);m=max2(m,c);m=max2(m,d);
return(m);}
intmax2(inta,intb)//max2函数的定义
{if(a>=b)returna;elsereturnb;}对max2
函数声明找a,b中较大者return(a>b?a:b);a,b中较大者a,b,c中较大者a,b,c,d中最大者main中调用max4函数,找4个数中最大者,max4中再调用max2,找两个数中的大者,max4中多次调用max2,可找4个数中的大者。intmax4(inta,int
b,int
c,intd)//max4函数{intmax2(inta,intb);
intm;m=max2(a,b);m=max2(m,c);
m=max2(m,d);
return(m);}intmax2(inta,intb)//max2函数{if(a>=b)returna;elsereturnb;}return(a>b?a:b);intmax2(inta,intb){
return(a>b?a:b);}程序改进1:可以将max2函数的函数体改为只用一个return语句,返回一个条件表达式的值。intmax4(inta,int
b,int
c,intd)//max4函数{intmax2(inta,intb);
intm;m=max2(a,b);m=max2(m,c);
m=max2(m,d);
return(m);}intmax2(inta,intb)//max2函数{return(a>b?a:b);}程序改进2:max4函数中,2个调用max2的语句:m=max2(a,b);可以用以下一行代替:(m=max2(max2(a,b),c);m=max2(max2(a,b),c);intmax4(inta,int
b,int
c,intd)//max4函数{intmax2(inta,intb);
intm;m=max2(a,b);m=max2(m,c);
m=max2(m,d);
return(m);}intmax2(inta,intb)//max2函数{return(a>b?a:b);}程序改进3:max4函数中,3个调用max2的语句:m=max2(a,b);可以用以下一行代替:(m=max2(max2(max2(a,b),c),d);即把函数调用作为函数的参数。m=max2(max2(max2(a,b),c),d);intmax4(inta,int
b,int
c,intd)//max4函数{
intmax2(inta,intb);
intm;m=max2(a,b);m=max2(m,c);
m=max2(m,d);
return(m);}intmax2(inta,intb)//max2函数{return(a>b?a:b);}程序改进4:甚至可以取消变量m,max4课写成:returnmax2(max2(max2(a,b),c),d);先调用max2(a,b),得到a和b中的大者,再调用max2(max2(a,b),c),(其中max2(a,b)为已知),得到a,b,c中的最大者,最后由max2(max2(max2(a,b),c),d);求得a,b,c,d中的最大者。returnmax2(max2(max2(a,b),c),d);§7.6函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。例如:
intf(intx)
{inty,z;
z=f(y);
return(2*z);
}图7.6图7.7在调用函数f的过程中,又要调用f函数,这是直接调用本函数,见图7.6在调用f1函数过程中要调用f2函数,而在调用f2函数过程中又要调用f1函数,见图7.7从图上可以看到,这两种递归调用都是无终止的自身调用。显然,程序中不应出现这种无终止的递归调用,而只应出现有限次数的、有终止的递归调用,这可以用if语句来控制,只有在某一条件成立时才继续执行递归调用,否则就不再继续。例7.6
有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁.问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人多大?显然,这是一个递归问题。解题思路:要求出第5个人的年龄,就必须先知道第4个人的年龄,而第4个人的年龄也不知道,要求出第4个人的年龄必须先知道第3个人的年龄,而第3个人的年龄又取决于第2个人的年龄,第2个人的年龄取决于第1个人的年龄。而且每一个人的年龄都比其前1个人的年龄大2。即:
age(5)=age(4)+2age(4)=age(3)+2age(3)=age(2)+2age(2)=age(1)+2age(1)=10
即
age(5)=age(4)+2
age(4)=age(3)+2
age(3)=age(2)+2
age(2)=age(1)+2
age(1)=10可以用数学公式表述如下:
可以看到,当n>1时,求第n个人的年龄的公式是相同的。因此可以用一个函数表示上述关系。图7.8表示求第5个人年龄的过程.
age(n)=10
(n=1)
age(n)=age(n-1)+2
(n>1)图7.8第一阶段是“回溯”过程第二阶段是递推过程求解过程可分两个阶段:第一阶段是“回溯”,即将第n个人的年龄表示为第(n-1)个人年龄的函数,而第(n-1)个人的年龄仍然不知道,还要“回溯”到第(n-2)个人的年龄……直到第1个人年龄。此时age(1)已知,不必再向前推了。图7.8第一阶段是“回溯”过程第二阶段是递推过程第二阶段,采用递推方法,从第1个人的已知年龄推算出第2个人的年龄(12岁),从第2个人的年龄推算出第3个人的年龄(14岁)……一直推算出第5个人的年龄(18岁)为止。一个递归的问题可以分为“回溯”和“递推”两个阶段.如果要求递归过程不是无限制进行下去,必须具有一个结束递归过程的条件。例如,age(1)=10,就是使递归结束的条件。可以用一个函数来描述上述递归过程:#include<stdio.h>intmain(){int
age(intn);printf("NO.5,age:%d\n",age(5));//输出第5人的年龄
return0;}int
age(intn)//定义递归函数
{intc;
if(n==1)
//如果n等于1c=10;
//年龄为10
else
//如果n不等于1
c=age(n-1)+2;
//年龄是前一人的年龄加2(如第4人年龄是第3人年龄加2)
return(c);}//返回年龄main函数中只有一个语句。整个问题的求解全靠一个age(5)函数调用来解决。从图7.9可以看到:age函数共被调用5次,即age(5)、age(4)、age(3)、age(2)、age(1)。其中,age(5)是main函数调用的,其余4次是在age函数中调用的,即递归调用4次。注意:在某一次调用age函数时并不是立即得到age(n)的值,而是一次又一次地进行递归调用,到age(1)时才有确定的值,然后再递推出age(2)、age(3)、age(4)、age(5)。例7.7用递归方法求n!。解题思路:求n!可以用递推方法,即从1开始,乘2,再乘3……一直乘到n。这种方法容易理解,也容易实现。递推法的特点是从一个已知的事实(1!=1)出发,按一定规律推出下一个事实(2!=2*1!),再从这个新的已知的事实出发,再向下推出一个新的实(3!=3*2!)。n!=n*(n-1)!这是和递归不同的。求n!··也可以用递归方法,即5!等于5×4!,而4!=4×3!…1!=1。可用递归公式表示:有了例7.6的基础,很容易写出本题的程序:n=
1
(n=0,1)
n.(n-1)!
(n>1)#include<stdio.h>intmain(){int
fac(intn);
//fac函数的声明
intn,y;
printf("inputanintegernumber:");
scanf(“%d”,&n);//输入要求阶乘的数y=fac(n);
printf("%lld!=%d\n",n,y);return0;}int
fac(intn)//fac函数的定义{intf;
if(n<0)//n不能小于0
printf("n<0,dataerror!");elseif(n==0||n==1)//n=0或1时,n!=1 f=1;elsef=fac(n-1)*n;//n>1时,n!=n*(n-1)
return(f);}main
fac(5)输出fac(5)fac函数n=5f=fac(4)*5fac(5)=120fac函数n=4f=fac(3)*4fac(4)=24fac函数n=3f=fac(2)*3fac(3)=6fac函数n=2f=fac1)*2fac(2)=2fac函数n=1f=1fac(1)=1§7.7数组作为函数参数前面已经介绍了可以用变量作函数参数,此外,数组元素也可以作函数实参,这就是说,凡是变量可以出现的地方,都可以用数组元素代替,其用法与变量相同。数组名也可以作实参和形参,传递的是数组首元素的地址。
§7.7.1数组元素作函数实参数组元素可以用作函数的实参,不能用作形参。因为形参是在函数被调用时临时分配存储单元的,不可能为一个数组元素单独分配存储单元(数组是一个整体,在内存中占连续的一段存储单元)。在用数组元素作函数实参时,把实参的值传给形参,是单向传递,即“值传送”方式。
例7.9
输入10个数,要求输出其中值最大的元素和该数是第几个数。解题思路:定义数组a,用来存放10个数;设计函数max,用来求两个数中的大者.在主函数中定义变量m,初值为a[0],每次调用max函数后的返回值存放在m中.用“打擂台”算法,依次将数组元素a[1]到a[9]与m比较,最后得到的m值就是10个数中的最大者。#include<stdio.h>intmain(){
int
max(int
x,inty);
//函数的声明
inta[10],m,n,i;
printf("enter10integernumbers:");
for(i=0;i<10;i++)//输入10个数给a[0]~a[9]
scanf("%d",&a[i]);
printf("\n");
for(i=1,m=a[0],n=0;i<10;i++){if(max(m,a[i])>m)
//若max函数返回的值大于m
{m=max(m,a[i]);
//max函数返回的值取代m原值
n=i;
//把此数组元素的序号记下来,放在n中
}
}
printf("Thelargestnumberis%d\nitisthe%dthnumber.\n",m,n+1);}
int
max(int
x,inty)//定义max函数{return(x>y?x:y);}//返回x和y中的大者§7.7.2数组名作函数参数除了可以用数组元素作为函数参数外,还可以用数组名作函数参数(包括实参和形参)。用数组元素作实参时,向形参变量传递的是数组元素的值;用数组名作函数实参时,向形参传递的是数组首元素的地址。例7.10有一个一维数组score,内放10个学生成绩,求平均成绩。解题思路:用函数average求平均成绩,用数组名作为函数实参,形参也用数组名,在average函数中引用各数组元素,求平均成绩并返回main函数。#include<stdio.h>intmain(){floataverage(floatarray[10]);
//函数声明,形参是数组
floatscore[10],aver;//定义实参数组
inti;
printf(“Pleaseinput10scores:\n");
for(i=0;i<10;i++)
scanf("%f",&score[i]);
printf("\n");aver=average(score);//函数调用,实参是数组名score
printf(“averagescoreis%5.2f\n",aver);return0;}定义实参数组float
average(floatarray[10])//函数定义,形参是数组名{inti;floataver,sum=array[0];
for(i=1;i<10;i++)sum=sum+array[i];
aver=sum/10;
return(aver);}相当于score[0]相当于scor
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年房产购买合同:交易条件与付款方式明细
- 出纳柜员年终工作总结范文(3篇)
- 2024年房屋租赁带装修合同
- DB4106T 5-2019 牛屠宰厂(场)建设管理规范
- 幼儿园大班下学期工作计划(27篇)
- 2024年挖掘机产品购买合同
- 2024年新形势下的供应链管理合作协议
- 2024年第二学期小学德育工作计划(5篇)
- 2024年影视剧本联合创作合同
- 2023年农业运输机械项目评价分析报告
- 工程结构通用规范(住建部2021年颁布)
- 混凝土监理旁站记录
- 临床医学老年性痴呆-课件
- 小学经典诵读社团活动计划、安排、记录汇编
- 咯血(课件幻灯)
- 《管理统计学》课程教学大纲
- C++语言基础知识
- 全国人工智能应用技术技能大赛理论考试题库大全-上(单选题汇总)
- 机关档案管理工作培训课件
- 工程施工阶段全过程造价控制与管理工作方案 精品
- 移动室内信号覆盖系统介绍演示文稿
评论
0/150
提交评论