第8 章函数.docx
- 文档编号:7299710
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:36
- 大小:28.80KB
第8 章函数.docx
《第8 章函数.docx》由会员分享,可在线阅读,更多相关《第8 章函数.docx(36页珍藏版)》请在冰豆网上搜索。
第8章函数
第8章函数
【内容摘要】
函数(分类、定义、声明、调用、参数与返回值)
函数的属性【内部函数与外部函数】
变量的属性(类别属性、存储属性)
工程文件(多文件的编译、连接、运行)
8.1函数的分类、定义、声明
一、函数的分类(不同分类方法将得到不同的结果)
1.从用户使用的角度分类,分为:
标准函数(即库函数);用户自定义函数;
2.从函数的参数的角度分类,分为:
有参函数;无参数函数;
二、函数的定义
1.无参函数的定义(形式):
类型标识符函数名(){声明部分;语句部分;}
【说明】①无参数函数后面的()不能省略;
②当函数的返回值为char(字符型)或int(整型)时,在定义时可以省略函数的类型标
名;
2.有参函数的定义(形式):
类型标识符函数名(形式参数列表){声明部分;语句部分;}
3.空函数的定义(形式):
类型标识符函数名(){}无声明部分和语句部分;
4.形式参数声明的方法:
传统方式:
intman(x,y)intx,y;{}
现代方式:
intmax(intx,inty){}
5.形式参数的作用:
①表示将从主调函数处接收哪些类型的数据;②在函数体中形参可以被引用;
【说明】①编译时形式参数不分配空间,在调用时才分配临时空间;
②形式参数的名称不重要,关键在于它类型和数量
三、函数的声明
1.声明的意义(或作用):
(P152)
把函数名称、类型、形式参数的类型、个数及顺序通知编译系统,以便在调用该函数时系统按此
进行对照比较,对被调函数的合法性进行检查。
2.函数声明的格式和位置:
(P153)即只给出函数的首部+;例:
floatadd(floatx,floaty);
【说明】①一般在主调函数的说明部分声明;也可在函数之外(被调用之前)声明;
②只有函数的返回值为char(字符型)或int(整型)时,声明时可以省略函数的类型标
识符;
3.函数原型及其一般格式:
(P153)
【说明】在函数的声明中也可以不写形参名,只写形参的类型,这种函数声明称之为函数原型。
例:
floatadd(float,float);
【说明】因为编译系统部检查函数名,故函数名有无都无所谓。
4.对被调函数不需声明的情形:
(P154)
①被调函数定义在主调函数之前;例:
intmax(intx,inty){}main(){max(x,y);}
②在所有函数定义之前,在函数的外部已作声明;
③函数的类型为int、char型时可不作声明(但实参和形参的类型应一致,否则可能出错)
四、函数的调用(P150)
1.函数调用的一般格式:
函数名(实际参数列表)(P150)
【说明】
①即使函数没有参数,但函数名后面的()不能省略。
②实际参数在类型、个数、顺序上要求和形式一致;实参与形参按顺序对应,一一传递数据;
③系统对数据参数的求值顺序不定;者将影响结果(TC中为从右到左)。
例:
(P150)
50
intsa(inta,intb){
if(a>b)return
(1);
elsereturn(a==b?
0:
-1);
}
main(){
inti=3;
printf(“”,sa(i,i++));
}
intsa(inta,intb){
return(a==b?
0:
a>b?
1:
-1);
}
main(){
inti=2;
printf("%d",sa(i,++i));
}
2.函数调用的方式和调用形式(P150)
1)调用的方式:
(1)函数语句。
例:
printf(“Thisisacprogram”);
(2)函数表达式。
例:
c=max(2,3);
(3)函数参数(函数作为其他函数的参数)。
例:
mx=max(a,max(b,c))
2)调用的形式:
(1)传值调用:
传递数据本身,实参与形参互不影响(实参与形参分别占用不同的存储空间)
(2)传址调用:
传递装载数据的空间的地址;实参与形参分别占用不同的存储空间
(3)嵌套调用:
函数之间互相调用
(4)递归调用:
函数自己调用自己
3.函数的传值调用
【传值调用过程】
①实参与形参分别占用不同的存储空间,形参值的改变不能传回给对应的实参;
②形参的存储空间是在函数被调用时分配的,然后将实际参数的值传递到该空间中。
③函数调用结束,形式参数所占的存储空间自动释放;
【实例1】voidswap(intx,inty){
x=x+y,y=x-y,x=x-y;
printf("swap:
x=%dy=%d\n",x,y);
}
main(){
inta=3,b=5;
swap(a,b);
printf("main:
a=%db=%d\n",a,b);
}
结果为:
swap:
x=5y=3
main:
a=3b=5
【实例2】用数组元素作为参数(实际参数,形式参数为变量)
main(){
voidswap(inta,intb);
inta[]={4,9};
printf("a[0]=%da[1]=%d\n",a[0],a[1]);
swap(a[0],a[1]);
printf("a[0]=%da[1]=%d\n",a[0],a[1]);
}
51
voidswap(inta,intb){
intt;
t=a,b=a,b=t;
}
结果为:
a[0]=4a[1]=9
a[0]=4a[1]=9
4.函数的传地址调用(例:
8.13,P166)
【传址调用过程】①实参与形参分别占用同一的存储空间,形参值的改变将传回给对应的实参中;
【实例1】用指针变量作为参数(形式参数)
voidswap(int*x,int*y){
*x=*x+*y;
*y=*x-*y;
*x=*x-*y;
printf("swap:
*x=%d*y=%d\n",*x,*y);
}
main(){inta=3,b=5;
swap(a,b);
printf("main:
a=%db=%d\n",a,b);
}
结果为:
swap:
*x=5*y=3
main:
a=5b=3
【实例2】用一维数组名作为参数(实际参数和形式参数)
main(){
voidswap(inta[2]);
inta[]={4,9};
printf("a[0]=%da[1]=%d\n",a[0],a[1]);
swap(a);
printf("a[0]=%da[1]=%d\n",a[0],a[1]);
}
voidswap(intb[2]){
intt;
t=b[0];
b[0]=b[1];
b[1]=t;
}
结果为:
a[0]=4a[1]=9
a[0]=9a[1]=4
【实例3】用数组名作为参数(实际参数用二维数组,形式参数用一维数组)
main(){
intmaxvalue(intb[]);
intn;
inta[][4]={{100,300,500,980},{12,24,36,57},{10,20,30,40}};
printf("max=%d\n",maxvalue(*a,12));
}
intmaxvalue(intb[],intn){
inti=0,k=0;
52
for(;i
returnb[k];
}
5.函数嵌套调用(P155):
voidsb(){
printf("Subfunction\'sb()\becalledbysunfunction\'sa()\'\n");
}
voidsa(){
sb();
printf("Subfunction\'sa()\becalledbysubfunction\'sa()\'\n");
}
main(){
printf("SubFunction\'sa()\'becalledbymainFunction.\n");
sa();
}
6.函数递归调用(P158):
在函数的过程中出现直接或间接调用该函数本身
1)直接递归调用:
在函数的过程中直接调用该函数本身。
【例1】求n!
(P160)
floatfac(intn){
floatf=1;
if(n<0){
printf("n<0,DataError");
exit
(1);}
if(n==0||n==1)f=1;
elsef=fac(n-1)*n;
return(f);
}
main(){
intn;
printf("n=?
");
scanf("%d",&n);
printf("%d!
=%-15.0f",n,fac(n));
}
floatfac(intn){
if(n<0){
printf("n<0,DataError");exit
(1);}
if(n==0||n==1)return
(1);
elsereturn(n*fac(n-1));
}
main(){
intn;
printf("n=?
");
scanf("%d",&n);
printf("%d!
=%-15.0f",n,fac(n));
}
【例2】要求不用循环语句(用直接递归),求1+2+3+┅┅+100(n属于正自然数)
longsum(intn){
if(n>0)return(n+sum(n-1));
elsereturn(0);
}
53
main(){
printf("sum=%ld",sum(100));
}
【例3】输入一字符串后,再反向输出(用直接递归)。
程序1:
voidconver(char*p){
charq=*p;
if(*p)conver(++p);
printf("%c",q);
}
main(){
char*p="";
printf("Inputastring:
");
gets(p);
conver(p);
}
程序2:
#include"stdio.h"
main(){
voidgt(char);
gt(getchar());
}
voidgt(chars){
if(s!
='\n')gt(getchar());
printf("%c",s);
}
【例4】求1-100的合计(用直接递归,n大于100,且由参数输入)。
程序1:
main(){
floatsum(int);
printf("sum=%f",sum(1000));
}
floatsum(inti){
if(i>0)return(i+sum(i-1));
elsereturn(0);
}
程序2:
实现系统函数strcmp()的功能
#include"string.h"
intsum=0;
voidum(inti,intn){
if(i<=n)um(sm(i),n);
}
intsm(inti){
sum=sum+i;return(i+1);
}
intscmp(chara,charb){/*实现系统函数strcmp()的功能*/
for(;*b&&*a;a++,b++)if(*a!
=*b)break;
return(*a-*b);
54
}
main(){chara[]="China",b[]="American";
printf("strcmp(%s,%s)=%d\n",a,b,scmp(a,b));
um(1,100);
printf("1+2+...+100=%d\n",sum);
}
【例5】Hanoi问题(用直接递归)(P161)。
【例6】求1!
+2!
+……+n!
(用直接递归)(P161)。
程序1:
floata=1,m=0;
voidsum(inti,intn){
if(i<=n)sum(ss(i),n);
}
intss(inti){
a=a*i;
m=m+a;
return(i+1);
}
main(){
inti=1,n=10;
sum(i,n);
printf("1!
+2!
+....+%d!
=%f",n,m);
}
程序2:
floatfac(inti){
floatp=1;
intk=1;
for(;k<=i;k++)p=p*k;
printf("%d!
=%f\n",i,p);
return(p);
}
main(){floatsum=0;
inti=1;
for(;i<=10;i++)sum=sum+fac(i);
printf("1!
+2!
+....+10!
=%f",sum);
}
程序3:
floatpt(intn){
if(n<=1)return
(1);
elsereturn(n*pt(n-1));
}
floatfat(inti){
if(i<=1)return
(1);
elsereturn(fat(i-1)+pt(i));
}
main(){
printf("1!
+2!
+....+10!
=%f",fat(10));
}
55
程序4:
floatfac(intn){
if(n>1)return(n*fac(n-1));
elsereturn
(1);
}
floatsum(intn){
if(n>0)return(fac(n)+sum(n-1));
elsereturn(0);
}
main(){
intn=1;
for(;n<20;n++)printf("%d!
+",n);
printf("%d!
=%f\n",n,sum(20));
}
程序5:
#defineN10
floatfas(intn){
staticfloatp=1;
p*=n;
return(p);
}
floatsm(intn){
if(n<=N)return(fas(n)+sm(n+1));
elsereturn(0);
}
main(){
intn=1;
for(;n +",n); for(n=1;n +",n); printf("%d! =%f\n",n,sm (1)); } 【例8】用循环和递归求任意两个正整数的最大公约数 intgcd(intm,intn){ if(m%n)gcd(n,m%n); elsereturn(n); } main(){inta=26,b=20,cs=0,r; printf("GCDINSUNF: %d\n",gcd(a,b)); a=20,b=26; while((r=a%b)! =0){ a=b; b=r; cs++;} printf("GCDINMAIN: %dcs: %d",b,cs); } #include"string.h" intsum=0; intscmp(char*a,char*b){ for(;*b&&*a;a++,b++)if(*a! =*b)break; 56 return(*a-*b); } voidum(inti,intn){ if(i<=n)um(sm(i),n); } intsm(inti){ sum=sum+i; return(i+1); } main(){ chara[]="China",b[]="American"; printf("strcmp(%s,%s)=%d\n",a,b,scmp(a,b)); um(1,100); printf("1+2+...+100=%d\n",sum); } 2)间接递归调用: 在函数的过程中直接调用其他函数,其他函数又调用该函数本身。 例: 五、函数的参数、返回值与属性(P164) 1.函数参数的类别(实际参数: 调用函数时传递;形式参数: 定义函数时指定): 1)实际参数: 简称为实参。 有两种形式: 值参数: 传递的是数值;址参数: 传递的是地址; (1)值参数的具体形式: 实参数的形式可以是: 常量、变量(含数组元素)、表达式、函数调用; (2)值参数的数据类型: int(6种)、char(2种)、float、void、结构体(或共同体)类型; 【说明】对应形式参数值的改变不影响实际参数的值;因它们分别占用不同的存储单元(内存空间); (3)址参数的具体形式: 指针、数组名(一维数组名或二维数组必须是列地址); (4)址参数的数据类型: 指针、数组; 【说明】①实参的数据类型: int(6种)、char(2种)、float、void、指针、数组名、结构体、共同体; ②对应形参数值的改变影响实参的值;因它们占用同一的存储空间; 2)形式参数: 简称形参,在定义函数时指定其名称和数据类型; (1)形参的具体形式: 只能是一般变量名、数组名、指针变量;不能是常量、表达式或函数调用; (2)形参的数据类型: int(6种)、char(2种)、float、void、结构体、共同体、指针、数组; 【说明】(P165) ①实际参数与形式参数的数据类型应该一致,否则可能发生类型转换,具体情形有: int(short,long)→unsignedint;int(short,long)→char;int→long; int(short,long)→float; double→float;char→unsignedchar及其逆运算; ②形参的形式只能是一般变量名或数组名或指针变量;实际参数的形式和类型要而与形式参数的形式 和类型对应(相容);当实参是值参时(形如常量、变量、数组元素、表达式、函数调用),对应 的形式参数只能是一般变量的形式;当实参是地址时(形如变量(数组元素)地址、数组名、指针 变量),对应的形式参数只能是地址形式(数组名或指针变量)。 ③实参数组与形参数组的大小可以不一致,因编译系统对此不做检查;只将其首地址传递给形参; ④形参数组的可以不指定大小,另设一参数传递其大小(当为字符数组时可以不传递数组的大小); ⑤当实参为址(指针、数组名)传递时,对应形式参值的改变要影响实参值的改变;因它们共同占用 同一存储单元(即内存空间)。 例: P166 ⑥实参为一般变量、数组名、指针变量时,应在主调函数中对其给予定义。 2.函数的类型 通常把函数返回值的类型称之为函数的类型,即定义时所指定的类型(函数的类型以定义的类型为 准)。 【说明】①int和char型函数在定义时可以不指定其类型(系统默然指定为整型) ②函数在定义时未指定其类型,系统默然指定其类型为整型 3.函数的返回值 【说明】 ①函数的返回值通过程序中的return(表达式)返回; 57 ②在程序中有多个表达式时,由被执行到的第一个return(表达式)返回函数的结果值; ③函数在返回时将返回值的类型自动转化为该函数定义的类型;即以定义的类型为准; ④函数可以没有返回语句。 ⑤当函数类型指定为void类型时,在函数中可有return语句,但不能返回表达式否则出错; 【函数递归实现的原理与递归终结条件】 函数的递归调用从某种意义上说相当与循环,但又有别于循环。 函数的递归调用就是在执行过程中,函 数的可执行部分尚未终止,接着又直接或间接的调用函数自己。 函数的递归调用分为直接递归和间接递 归。 直接递归就是一次函数的执行尚未结束,在本身的函数体中又调用自己。 函数直接递归调用的关键是: 怎样才能实现递归调用? 怎样使递归调用终结? 终结条件是什么? 1)实现有条件的递归调用,即当条件满足时函数才递归调用,否则就结束递归调用。 2)递归调用的终结条件: (1)至少有一次不用递归调用的情况,关键是在不同的情况如何实现。 (2)递归实现的条件要能够向非递归转化。 要不然可能造成无限的递归调用; 例: 输入若干字符,以回车作为输入结束标志。 要求反向输出。 若输入为: ABCDEFGHIJK↙ 要求输出: KJIHGFEDCBA 自己定义函数如下: voidsa(charch){ if(ch! ='\n')sa(getchar());/*递归实现的条件ch! ='\n'*/ printf(“%c”,ch);/*自动变量的特点,不同函数中的同名变量互不影响*/ } 请注意该递归调用过程中递归条件是怎样向非递归转化的? 思考题: 将上述函数改为如下程序后,能否实现上述要求? 为什么? voidsa(charch){ if(ch! ='\n')sa(getchar());/*递归实现的条件ch! ='\n'*/ elseprintf(“%c”,ch); } 【问题】利用静态变量和自定义函数与函数的递归调用实现求1-100的合计,不能用循环结构。 8.2函数的属性(内部函数与外部函数) 根据函数能否被其他函数调用,将函数分为内部函数和外部函数 一、内部函数 1.概念: 如果一个函数只能被本文件中的其他函数所调用,则称之为内部函数;又称之为静态函 数。 2.定义格式: static类型名称函数名称(形式参数列表) 【说明】不同文件之间的静态同名函数互不影响。 二、外部函数 1.概念: 如果一个函数能被本文件外的其他函数所调用,则称之为外部函数;又称之为全局函数。 2.定义格式: extern类型名称函数名称(形式参数列表) 【说明】在需要调用此函数的文件中,用extern声明所用的函数为外部函数。 三、多个源文件的编译、连接(P184)【工程文件的建立】 1.先编辑每个需要的源文件 2.编辑工程文件(即项目文件)(.prj);工程文件中只包含需要连接、编译的每个源文件名称即 可。 3.通过alt+p打开主菜单,选择“ProjectName”,输入工程文件名 4.最后编译为一个可执行文件 【外部函数调用实例分析: 多个源文件的连接、编译】 58 /*enterab.prj文件的内容: */ entabgetmaxgetmingetsum /*源程序文件entab.c的内容*/ main(){ inta,b; /*externintmax(int,int); externintmin(int,int); externintsum(int,int);*/ printf("Inputab: ");scanf("%d%d",&a,&b); printf("max=%d\nmin=%d\nsum=%d\n",max(a,b),min(a,b),sum(a,b)); } /*源程序文件getmin.c的内容*/ intmin(inta,intb){ return(a>b? b: a); } /*源程序文件getmax.c的内容*/ intmax(inta,intb){ return(a>b? a: b); } /*源程序文件getsum.c的内容*/ intsum(inta,intb){ return(a+b);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第8 章函数 函数