第8讲第八章 函数.docx
《第8讲第八章 函数.docx》由会员分享,可在线阅读,更多相关《第8讲第八章 函数.docx(26页珍藏版)》请在冰豆网上搜索。
第8讲第八章函数
第八讲函数
一、授课内容
1、科目:
C语言程序设计
2、授课内容:
第八章 函数
3、授课类型:
讲授
4、授课时间:
10学时
5、主讲教师:
肖飞
二、学目的与要求
1、掌握C语言函数的使用
三、教材分析
a)教材概述:
本部分内容讲述C语言的函数的使用
b)教学重点:
C语言的函数的特点
c)教学难点:
熟练掌握函数编程
二、教学设想
a)教材处理设想:
示例、例题、比喻、比较
b)教学方法设想:
讲授为主,辅以常识、实际操作指导
c)教具运用设想:
电子幻灯片
三、教学过程
导入:
在设计复杂的问题时,一般采用的方法时:
把问题分成几个部分,每个部分又分成若干个小部分,逐步细化,直至分解成很容易的小问题。
求解较小问题的算法和程序称为“功能模块”。
一个解决大问题的程序,可以分解成多个小问题模块。
各个功能模块单独设计,然后将小模块组合求解大问题。
这就时“自顶向下”的模块化设计方法。
8.1概述
一个较大的程序可以分为若干个子模块,每一个模块执行一个特定的功能
例编程实现下列图案
****************************
howdoyoudo!
****************************
****************************
howdoyoudo!
****************************
****************************
howdoyoudo!
****************************
方案一:
用一个主函数来实现
方案二:
设计一个函数:
main()
{print();
print();
print();
}
voidprint()
{printf(“****************************”);
printf(“howdoyoudo!
”);
printf(“****************************”);
}
说明:
(1)一个源程序文件由一个或多个函数组成
(2)一个C程序由一个或多个源程序文件组成
(3)C程序的执行从main()函数开始,调用其它函数后返回到main()函数
(4)所以函数都是平行的
(5)从用户角度看,函数有两种
1库函数
2用户自定义函数
(6)从函数形式看,函数分两类
1无参函数
2有参函数
8.2函数定义的一般形式
一、无参函数的定义形式为:
类型标识符函数名()
{声明部分
语句
}如:
上面的print()属于无参函数
二、有参函数定义的一般形式为
类型标识符函数名(形式参数表列)
{声明部分
语句
}
voidmain()
{inta,b,m;
scanf(“%d,%d”,&a,&b);
m=max(a,b);
}
例如:
intmax(intx,inty)
{
intz;
z=x>y?
x:
y;
return(z);
}
三、可以有“空函数”
类型说明符函数名()
{}
四、对形参的声明的传统方式
例:
intmax(x,y)
intx,y;
{}
8.3函数参数和函数的值
一、形式参数和实际参数
在调用函数时,大多情况下,主调函数和被调函数之间有数据传递关系。
例:
8.2
voidmain()
{inta,b,c;
scanf(“%d,%d”,&a,&b);
c=max(a,b);
printf(“maxis%d”,c);
}
intmax(intx,inty)
{intz;
z=x>y?
x:
y;
return(z);
}
说明:
(1)在函数中指定的形参,在未出现函数调用时,它们并不占用内存中的存储单元
(2)实参可以是常量、变量或表达式
(3)在被定义的函数中,必须指定形参的类型
(4)实参和形参的类型相同或赋值兼容
(5)C语言规定,实参变量对形参变量的数据传递是“值传递”,即单向传递
值传递的概念要重点讲述:
voidmain()
{inta,b;
a=3;b=5;
change(a,b);
printf(“a=%d,b=%d\n”,a,b);
}
举例:
voidchang(intx,inty)
{
intt;
t=x;x=y;y=t;
}
二、函数的返回值
(1)函数的返回值是通过return语句获得的,且只能返回一个值
(2)函数值的类型定义函数时决定如:
charletter(charc1,charc2)
(3)如果函数值的类型与return语句中的表达式的值不一致,则以函数类型为标准见例8.3
main()
{floata,b;
intc;
scanf(%f,%f”,&a,&b);
c=max(a,b);
printf(“%d”,c);
}
max(floatx,floaty)
{floatz;
z=x>y?
x:
y;
return(z);
}
(4)如果被调函数中没有return语句,并不带会一个却确定的值
(5)为了表示“不带回值”,可以用void定义如:
print()函数
8.4函数的调用
一、函数调用的一般形式为:
函数名(实参表列)
例8.4
intf(inta,intb)
{intc;
if(a>b)c=1;
elseif(a==b)c=0;
elsec=-1;
return(c);
main()
{inti=2,p;
p=f(i,++i);
printf(“%d”,p);
}Turboc上按从左至右顺序求值,结果为-1可令k=++i,消除歧义
二、函数调用的方式
1、函数语句print();
2、函数表达式c=2*max(a,b);
3、函数参数m=max(a,max(b,c));
三、对被调用函数的声明和函数原形
在一个函数中调用另外一个函数具备的条件:
(1)被调用的函数必须存在
(2)如果使用库函数,则必须加上头文件
(3)如果使用自己定义的函数,而且该函数与调用它的函数在一个文件中,一般还应该在主调函数中对被调函数作声明
例:
8.5
main()
{floatadd(floatx,floaty);对被调函数的声明
floata,b,c;
……………………
}
floatadd(floatx,floaty){}
函数原形一般形式为:
(1)函数类型函数名(参数类型1,参数类型2…………)
(2)函数类型函数名(参数类型1参数名1,参数类型2参数名2)
说明:
(1)以前C版本的函数声明不采用函数原形
(2)如果在调用函数前,没有对函数作声明,则编译系统会把第一次遇到的该函数形式作为函数的声明,并将函数默认为int型
(3)如果被调用函数的定义在主调函数之前,可以不必加以声明
(4)如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必对所调用的函数再做声明
8.5函数的嵌套调用
C语言的函数定义都是相互平行的,也就是或不允许在一个函数内定义另外一个函数,这和Pascal不同。
C语言不能定义嵌套函数,但可以嵌套调用。
C语言嵌套调用函数过程如图8、5
Main函数a函数b函数
调用a函数调用b函数
结束
例:
voidprint_star()
{printf(“************************\n”);}
voidprint()
{print_star();
printf(“Howdoyoudo!
\n”);
print_star();
}
main()
{print();
print();
}
例:
求两个正整数的最大公约数:
inthcf(intu,intv)intlcd(intu,intv)
{intt,r;{intt,p;
if(v>u){t=u;u=v;v=t;}t=hcf(u,v);
while((r=u%v)!
=0)p=u*v/t;
{u=v;v=r;}return(p);}
return(v);
}
main()
{intm,n,p;
scanf(“%d,%d”,&m,&n);
p=lcd(m,n);
printf(“LCD=%d”,p)
}例:
用弦截法求方程f(x)=x3-5x2+16x-80=0
floatxpoint(floatx1,floatx2)
{floatx;
x=(x1*f(x2)-x2*f(x1))/(f(x2)-f(x1));
return(x);}
#include
floatf(floatx)
{floaty;
y=((x-5.0)*x+16.0)*x-80.0;
return(y);}
main()
{floatx1,x2,f1,f2,x;
do{scanf(“%f,%f”,&x1,&x2);
f1=f(x1);f2=f(x2);
}while((f1*f2)>=0)
x=root(x1,x2);
printf(“Arootofequationis%8.4f”,x);
}
floatroot(floatx1,floatx2)
{inti;floatx,y,y1;
y1=f(x1);
do{x=xpoint(x1,x2);
y=f(x);
if(y*y1>0)
{y1=y;x1=x;}
elsex2=x;
}while(fabs(y)>=0.0001);
return(x);
}
8.6函数的递归调用
在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。
例:
intf(intx)
{inty,z;
z=f(y);
return(2*z);
}
f函数f1函数f2函数
调用f函数调用f2函数调用f1函数
图8.9图8.10
例8.7有5个人坐在一起,问第5个人多少岁?
他说比4个人大2岁。
问第4个人岁数,他说比第3个人大2岁,问第3个人,又说比第2个人大2岁。
问第2个人,说比第1个人大2岁。
最后问第一个人,他说是10岁。
请问第5个人多大。
即:
age(5)=age(4)+2
age(4)=age(3)+2
age(3)=age
(2)+2
age
(2)=age
(1)+2
age
(1)=10
递归关系图见课本159页图8.11
函数如下:
age(intn)
{intc;
if(n==1)c=10
elsec=age(n-1)+2
return(c);
}main()
{printf(“%d”,age(5));}
例8.8求n!
1(n=0,1)
n!
n*(n-1)!
(n>1)
程序如下:
floatfac(intn)
{
floatf;
if(n<0){printf(“n<0,dataerror!
”);}
elseif(n==0||n==1)f=1;
elsef=fac(n-1)*n
return(f);
}
main()
{
intn;
floaty;
printf(“inputanintegernumber:
”);
scanf(“%d”,&n);
y=fac(n);
printf(“%d!
”=%10.0f”,n,y);}
例:
8.9Hanoi(汉洛)塔问题:
有一座塔,塔内有3个座A、B、C,开始A座上有64个盘子,盘子大小不等,大的在下,小的在上。
现在老和尚想把A座上的盘子,移到C座。
要求没有只能移动一个盘子,并且始终要保持大盘子在下,小盘子在上。
移动方案:
(1)老和尚命令第2个和尚把63盘子从A座移到B座
(2)自己将一个盘子从A座移到C座
(3)再命令第2个和尚把63个盘子从B座移到C座。
同理:
第2个和尚又命令第3个和尚把62个盘子从A座移到B座,自己把第63个盘子从A座移到C座,再命令第3个和尚把62个盘子从B座移到C座。
一层层递归。
程序如下:
(程序说明盘子的移动过程)
voidmove(charx,chary)
{printf(“%c-%c\n”,x,y);}
voidHanoi(intn,charone,chatwo,charthree)
{if(n==1)move(one,three);
else
{hanoi(n-1,one,three,two);
move(noe,three);
Hanoi(n-1,two,one,three);
}
}
main();{intm;scanf(“%d”,&m);Hanoi(m,’A’,’B’,’C’);}
第七节数组作为函数参数
可用用常量变量作为数组的参数。
数组也可以作为函数的参数
一、数组元素作函数实参
例:
8.10
有两个数组a、b,各有10个元素,将它们对应地逐个相比。
如果a数组中的元素大于b数组中相应元素的数目,则认为a数组大于b数组,并分别统计出两个数组相应元素大于、等于、小于的次数
程序如下:
main()
{intlarge(intx,inty);
inta[10],b[10],I,n=0,m=0,k=0;
printf(“enterarraya:
\n”);
for(I=0;I<10;I++)
scanf(“%d”,&a[I]);
printf(“\n”);
printf(“enterarrayb:
\n”);
for(I=0;I<10;I++)
scanf(“%d”,&b[I]);
printf(“\n”);
for(I=0;I<=10;I++)
{if(large(a[I],b[I])==1)n=n+1;
elseif(large(a[I],b[I])==0)m=m+1;
elsek=k+1;
printf(“a[I]>b[I]%dtimes\na[I]=b[I]%dtimes\na[I]
if(n>k)printf(“arrayaislargerthenarrayb\n”);
elseif(nelseprintf(“arrayaisequaltoarrayb\n”);
}
large(intx,inty)
{intflag;
if(x>y)flag=1;
elseif(xelseflag=0;
return(flag);
}
二、数组名可作为函数参数
数组名作为函数参数,此时实参与形参都应用数组名
例:
8、11有一个数组score,内放10个学生成绩,求平均成绩
floataverage(floatarray[10])
{intI;
floataver,sum=array[0];
for(I=1;I<10;I++)
sum=sum+array[I];
aver=sum/10;return(aver);}
main()
{floatscore[10],aver;
intI;
for(I=0;I<10;I++)
scanf(“%f”,&score[I]);
aver=average(score);
printf(“%5.2f”,aver);
}
说明:
(1)用数组名作函数参数,应该在调用函数和被调用函数分别定义数组
(2)实参数组与形参数组类型必须一致,否则结果将出错
(3)对形参数组指定大小没有任何意义,实际上系统对形参数组不专门分配地址空间,只是将实参数组的首地址传递给形参,实参数组和形参数组指向同一个内存单元。
(4)形参数组可以不指定大小,如果为了在调用函数中知道实参数组元素的个数,可以另加一个变量n。
例:
8、12p166
例如:
floataverage(floatarray[],intn)
(5)用数组名做函数实参时,不是把数组元素的值传递给形参,而是把实参数组的起始地址传递给形参数组,这样两个数组指向同一个个内存单元。
例:
8、13用选择法对10个整数按由小到大排序
三、用多维数组名做函数参数
对形参数组定义可以省略第一维的长度,但不能省略第二维的长度。
例:
8、14有一3*4矩阵,求所有元素的最大值
max_value(intarray[][4])
{intI,j,k,max;
max=array[0][0];
for(I=0;I<3;I++)
for(j=0;j<4;j++)
if(array[I][j]>max)max=array[I][j];
return(max);
}
main()
{inta[3][4]={{1,3,5,7},{2,4,6,8},{15,13,15,13}};
printf(“%d”,max_value(a));
}
8.8局部变量和全局变量
一、局部变量
在一个函数内部定义的变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,称为局部变量。
说明:
(1)在主函数中定义的变量也只在主函数中有效
(2)不同函数可以使用相同名字的变量,代表不同的对象,互不干扰
(3)形式参数也是局部变量。
(4)在一个函数内部,可以在复合语句中定义变量,这些变量只在复合语句中有效
二、全局变量
在函数外部定义的变量成为全局变量
说明:
(1)全局变量增加了函数间数据联系的渠道
(2)建议不在必要时不要使用全局变量
1)全局变量在整个执行过程中,多要占用存储单元
2)它使函数的通用性降低
3)使用全局变量过多,回降低程序的可读性
(3)如果在同一个文件中,外部变量和局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。
例:
8、16
inta=3,b=5;
max(inta,intb)
{intc;
c=a>b?
a:
b;
return(c);
}
main()
{inta=8;
printf(“%d”,max(a,b));
}
8.9变量的存储类别
一、动态存储方式与静态存储方式
从变量值存在的时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式
内存中的供用户使用的存储空间的情况。
这个存储空间可以分为三个部分
1、程序区
2、静态存储区如:
全部变量
3、动态存储区形式参数自动变量函数调用的现场保护
存储方式分为两大类:
静态存储类和动态存储类。
具体包括四种:
1、自动的
2、静态的
3、寄存的
4、外部的
二、auto变量
在调用该函数时系统给他们分配存储空间,在函数调用结束时就自动释放这些存储空间。
称为自动变量。
例如:
intf(inta)
{autointb,c=3;
}
这里的auto可以省略,程序执行完以后自动释放a、b、c所占的存储单元
三、用static声明局部变量
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即
其占用的存储单元不释放。
例8.17
f(inta)
{intb=0;
staticc=3;
b=b+1;
c=c+1;
return(a+b+c);
}
main()
{inta=2;i;
for(i=0;i<3;i++)
printf(“%d”,f(a));
}
对静态局部变量的说明:
(1)静态局部变量属于静态存储类别,在静态存储区内存分配单元。
在整个程序运行期间都不释放。
(2)对静态局部变量是在编译时赋初值的,即只赋初值一次,以后每次调用函数不在重新赋初值,而是保存上一次的值
(3)对于定义局部变量不赋初值的话,则对静态局部变量来说,编译时自动赋初值为0或空字符。
对于自动变量来说,如果不赋初值则它的值是一个不确定的值
(4)虽然静态局部变量在函数调用结束后仍然存在,在其他函数是不能引用它。
什么时候用到静态变量呢?
见课本175页例8.18
四、register变量
当有些变量使用频繁,则存取变量可以采用寄存器变量见课本175页例8.19
说明:
(1)只有局部自动变量和形式参数可以作为寄存器变量
(2)一个计算机系统中的寄存器数目是有限的,不能任意定义多个寄存器变量
(3)局部静态变量不能定义为寄存器变量
五、用extern声明外部变量
1、在一个文件内声明外部变量
如果我们定义的外部变量不在文件开头,其有效范围只限于定义处到文件结束。
如果在此之前要引用它,则应该在引用之前用关键字extern对该变量作“外部变量声明”。
例8.20
intmax(intx,inty)
{intz;
z=x>y?
x:
y;
return(z);}
main()
{externintA,B;
printf(“%d”,max(A,B));
}
intA=13,B=8;
2、在多文件中的程序中声明外部变量
如果一个程序包含了两个文件,在两个文件中同时引用一个外部变量Num,不能分别在两个文件中定义一个外部变量,可以用在一个程序中用extern进行声明。
例8、21
intA;
main()
{intpower(int);
scanf(“%d,%d”,&A,&m);
c=A*b;
d=power(m);
printf(“%d*%d=%d”,A,m,d);}
六、用static声明外部变量
有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用,可以在定义外部变量时加一个static声明
file1.cfile2.c
staticintA;externintA;
则file2.c就不能引用file1.c中的全局变量A.
七、关于变量的声明和定义
如:
externA是对变量的声明
intA是对变量的定义
s