版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章函数本章要求理解模块化程序设计思想;
掌握函数的定义与调用方法;掌握C语言的参数传递方式,尤其是数组参数的传递特点;掌握变量的作用域和存储方式。本章重点函数的定义与调用参数传递变量的作用域和存储方式本章难点参数传递变量的作用域和存储方式第6章函数1第6章函数函数概述函数的定义函数调用与参数传递函数与指针main函数的参数函数与变量的作用域应用程序举例第6章函数26.1
函数概述C语言源程序是由函数组成的。虽然在前面的程序中都
只有一个主函数main(),但实用程序往往由多个函数组成。函数是C源程序的基本模块,通过对函数模块的调用实现特定的功能。可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函数式语言。C语言不仅提供了极为丰富的库函数(如TurboC提供了三百多个库函数),还允许用户建立自己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。第6章函数36.1.1
模块化程序设计方法模块是较小的、能够完成一定任务的、相对独立的程序段,可以被看作组成一个程序的逻辑单元。C语言使用函数作为程序的组成单元。简化程序设计便于调试和维护使用函数进行程序设计时,一个完整的程序由一个主函数main和若干个其它函数组成,由主函数根据需要调用其它函数来实现相应功能,调用的关键在于函数间的数据传递。对于每一个函数,它仍然由顺序、选择和循环三种基本结构组成。6.1
函数概述第6章函数45
第6章函数#defineNAME
"Zhejiang
Unversity
ofScience
and
Technology"#defineADDRESS
"1
Xiaoheshan
Road,Hangzhou,
P.R.China"#definePOSTCODE
"310023"voidline(void);void{main()/*声明函数原型*//*调用函数*//*调用函数*/line();printf("%s\n",NAME);printf("%s\n",ADDRESS);printf("%s\n",POSTCODE);line();}void
line(void)
/*定义函数*/{
printf("======================================\n");}例.
编写和使用一个简单的函数6.1
函数概述第6章函数66.1.2
C语言函数的分类1.
从函数定义的角度看,函数可分为库函数和用户定义函数库函数由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可直接调用。在前面反复用到printf
、scanf
等函数均属此类。用户定义函数由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。6.1
函数概述第6章函数72.C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回
值函数有返回值函数此类函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。如数学函数即属于此类函数。由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明中明确返回值的类型。无返回值函数此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此类函数时可指定它的返回为“空类型”,空类型的说明符为“void”。6.1
函数概述3.从主调函数和被调函数之间数据传送的角度看可分为无参函数和有参函数两种无参函数函数定义、函数说明及函数调用中均不带参数。主调函数和被调函数之间不进行参数传送。此类函数通常用来完成一组指定的功能,可以返回或不返回函数值。有参函数(带参函数)在函数定义及说明时都有参数,称为形式参数(形参)。在函数调用时也必须给出参数,称为实际参数(实参)。进行函数调用时,主调函数将把实参的值传送给形参,供被调函数使用。第6章函数86.1
函数概述第6章函数9说明:1、在C语言中所有的函数定义,包括主函数main在内,都是平行的。也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。2、函数之间允许相互调用,也允许嵌套调用。把调用者称为主调函数。函数还可以自己调用自己,称为递归调用。3、一个C源程序必须有,也只能有一个主函数main。4、main
函数是主函数,它可以调用其它函数,而不允许被其它函数调用。5、C程序的执行总是从main函数开始,完成对其它函数的
调用后再返回到main函数,最后由main函数结束整个程序。6.1
函数概述6.2
函数的定义类型符使用数据类型标识符,说明函数返回值的类型。函数名是一个标识符,应当遵循C语言中标识符的命名规则。形式参数可以是0个、一个或多个,表示该函数被调用时所需的一些必要信息。对于有参函数,形式参数的定义与变量的定义形式相似。函数体是一组放在一对花括号中的语句,一般包括声明部分和执行部分。在函数中,形式参数可作为已知的变量,不需要对它赋值就可
使用它,它的值是函数调用时由实参传递给形参变量而得到的。6.2.1
函数的定义形式类型符函数名([形式参数]){函数体}第6章函数10例如,定义一个函数,用于求两个数中的大数,可写为:int
max(int
a,
int
b){
intz;if
(a>b)
z=a;else
z=b;return
(
z
);}现代格式int
max(a,b)int
a,b;{
int
z;if
(a>b)
z=a;else
z=b;return
(
z
);}传统格式第6章函数116.2
函数的定义/*调用函数*/第6章函数12{
line('*',45);printf("%s\n",NAME);printf("%s\n",ADDRESS);printf("%s\n",POSTCODE);line('*',45);/*调用函数*/}void
line(char
c,int
n)/*定义函数*/{ int
i;for(i=1;i<=n;i++)putchar(c);printf("\n");}例.
有参函数的定义与使用。#define
NAME
"Zhejiang
Unversity
of
Science
and
Technology"#define
ADDRESS
"1
Xiaoheshan
Road,
Hangzhou,
P.R.China"#define
POSTCODE
"310023"void
line(char
c,int
n);
/*声明函数原型*/void
main()6.2
函数的定义6.2.2
函数返回值函数的返回值是指函数被调用之后,执行函数体中的程序段所得的并返回给主调函数的值。C语言函数的值只能通过return语句返回函数的值。1.
return语句返回函数的值return
语句使用的一般形式:return
表达式;或者为:return(表达式);return该语句的功能是计算表达式的值,并返回给主调函数。第6章函数136.2
函数的定义在函数中允许有多个return语句,但每次调用只能有一个,执行到哪一条return语句,哪一条起作用。因此只能返回一个函数值。return语句的另一作用是终止执行函数。表达式的值即函数返回值,它应与所定义的函数返回值的类型一致。建议只在函数结尾处使用一次return语句。函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类型为准,自动进行类型转换。若函数值为整型,在函数定义时可以省去类型说明。不返回函数值的函数,可以明确定义为“空类型”,类型说明符为“void”。第6章函数146.2
函数的定义第6章函数15int
max(int
x,int
y);
/*声明函数原型*/void
main(){ int
a,b;scanf("%d,%d",&a,&b);switch(max(a,b)) /*调用函数*/{case
1:
printf("x>y");break;case
-1:
printf("x<y");break;case
0:
printf("x=y");break;}}int
max(int
x,int
y) /*定义函数*/{ if
(x>y)
return
1;else
if
(x<y)
return
-1;return
0;}函数返回值做条件6.2
函数的定义6.2.3
形式参数的设计设计形式参数应从函数的功能分析入手,哪个数据需要调用函数提供,这就应定义一个形式参数接收该数据。函数设计原则(“黑箱”观点)6.2
函数的定义所有的输入是以参数的形式传递给函数的所有的输出是以函数值的形式返回函数中使用的变量是局部变量,与主调函数没有任何关系第6章函数16第6章函数176.2
函数的定义例.
编写一个函数求两个整数的最大公约数。int
maxgynum(int
n,
int
m){
int
r,t;if
(m<n) {
t=m;
m=n;
n=t;
}r=m%n;while
(r!=
0){
m=n;
n=r;
r=
m%n;
}return
n;}void
main(){
int
n,m,gyshu;printf("Enter
m,n=?");scanf("%d%d",&m,&n);gyshu
=
maxgynum(
n,
m);printf("The
max
Gyshu=%d\n",gyshu);}6.2.4
函数原型在使用自定义函数时,除了进行函数的定义外,还需要在调用该函数之前对其进行原型声明。函数原型声明和函数定义的区别函数原型声明的作用是将函数类型告诉编译系统,使程
序在编译阶段对调用函数的合法性进行全面的检查,避
免函数调用时出现参数的个数或类型不一致的运行错误。函数定义部分则是函数的实际实现代码。函数原型声明的格式:类型符函数名(形式参数);6.2
函数的定义第6章函数18第6章函数19说明:函数原型声明语句最后的分号不能省略,其作用是表示该语句是进行函数声明而不是函数定义。在原型声明中,形式参数名可以省略。如果被调用函数的定义出现在调用函数之前,可以不对被调用函数进行原型声明。如果被调用函数已在所有函数定义之前进行了原型声明,则在各个调用函数中不必再对该函数进行原型声明。6.2
函数的定义第6章函数20main(){
int
x,y,z;int
max(int
a,int
b);//或者int
max(int,int)scanf("%d%d",&x,&y);z=max(x,y);printf("max=%d\n",z);}int max(int
a,
int
b){
intd;d=a>b?a:b;return
(d);}例如:6.2
函数的定义例如:char
str(int
a);float
fy(float
b);main(){char
str(10);float
fy(5.5);……}char
str(int
a){
……}float
fy(float
b){
……}放在最前面说明第一,二行对str函数和
fy函数预先作了说明。因此在以后各函数中无须对str和f函数再作说明就可直接调用。第6章函数216.2
函数的定义第6章函数22实验:实验手册实验七要求:编写第1、3(第3题用指针的方法)题的代码做在实验报告上第1、3题电子稿上传其余题目在上课时进行调试,结果写在实验手册上6.3
函数调用与参数传递6.3.1
函数的调用方式有参函数调用的格式:函数名(实际参数)无参函数的调用格式:函数名()说明函数总是在某个函数体中被调用函数调用可以在结尾处加上分号,单独作为一条语句对于有返回值的函数,其调用也可出现在某条语句中第6章函数23函数的调用过程6.3
函数调用与参数传递第6章函数246.3.2
参数传递形式参数与实际参数的区别形式参数又称为形参或虚参,它表示一个函数被调用时所需的一些必要信息。事实上,形参是一种局部变量,它在函数被调用前并没有被分配存储空间,也没有具体的值。形参仅仅是一个“符号”。实际参数又称为实参,它是调用函数向被调用函数提供的一些具体信息,可以是常量、变量或表达式,它有具体的值。对于实参变量而言,它已经被分配了相应的存储空间。第6章函数256.3
函数调用与参数传递调用有参函数时,必须要提供与形参相匹配的实参。所谓匹配是指实参与形参的个数相等,对应实参与形参的类型相同或赋值兼容。在调用有参函数时,系统将会根据形参的类型为其分配存储空间,而存储空间中的内容即形参的值,则来自调用函数所提供的实参。当被调用函数执行完毕返回调用函数时,形参的存储空间又被系统收回,形参的值也就不复存在了。注意,C语言中的参数传递是一种单向的“值传递”。当实参为变量时,函数调用时仅仅是将实参变量的值复制了一份交给形参,形参与对应实参的存储空间完全不同,在函数调用过程中对形参的改变,根本不会影响到实参的值。第6章函数266.3
函数调用与参数传递例:main(){
int
x,y,z;scanf("%d%d",&x,&y);z=max(x,y);int max(int
a,
int
b){ int
d;d=a>b?a:b;return
(d);}printf("max=%d\n",z);}函数的形参和实参具有以下特点:第6章函数276.3
函数调用与参数传递1.
形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。第6章函数286.3
函数调用与参数传递实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。实参和形参在数量上,类型上,顺序上应严格一致,否则会发生“类型不匹配”的错误。函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参的值不会变化。/*声明函数原型*/第6章函数29void
swap(int
a,int
b);void
main(){int
x=10,y=20;printf("Before
swapping:x=%d
y=%d\n",x,y);swap(x,y);
/*调用函数*/printf("After
swapping:x=%d
y=%d\n",x,y);}/*定义函数*/void
swap(int
a,int
b){int
t;t=a;
a=b;
b=t;}6.3
函数调用与参数传递例:参数传递。程序输出如下:Before
swapping:x=10
y=20After
swapping:
x=10
y=206.3
函数调用与参数传递第6章函数30第6章函数31参数传递小结形参出现在函数定义中,在函数体内都可以使用,函数未被调用时,系统不给它分配存储空间,当函数被调用时系统才给分配其储存空间,用来接受从主调函数传递过来的数据,当该函数调用结束后,则立即释放其储存空间。实参出现在主调函数中,即调用时写入函数括号中的参数,调用时将实参数据传递给对应的形参,进入被调函数后,实参变量不能使用。形参与实参的的关系:形参和实参的功能是作数据传递。发生函数调用时(形、实结合),主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。6.3
函数调用与参数传递调用函数的几种形式:函数表达式函数作表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。例如:
z=max(x,y)
是一个赋值表达式,把max函数的返回值赋予变量z。函数语句函数调用的一般形式加上分号即构成函数语句,即:函数名(实际参数表);例如:
printf
("%d",a); scanf("%d",&b);都是以函数语句的方式调用函数。第6章函数326.3
函数调用与参数传递6.3
函数调用与参数传递3.函数实参函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。例如:
printf("%d",max(x,y));
即是把max调用的返回值又作为printf函数的实参来使用的。第6章函数33调用函数的几种形式:第6章函数34练习判断题若main函数无返回值,定义函数main时可以缺省标识符“void”。函数中不可以没有return语句。传值调用的形参只有在被调用示才被创建(分配存储单元)。传值调用时,实参不限于变量名,而且可以是表达式。被定义为前向调用的函数,不必再声明其函数原型。程序中不能用同一个函数名定义不同的函数。第6章函数35程序填空:程序在输入平面上3个点的坐标后,调用函数length计算并输出各点之间的距离。(1)float
length(float
x1,
float
y1,
float
x2,
float
y2){ float
t;t=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))return
t;}void
main(){
(2)scanf("%f%f%f%f%f%f",&ax,&ay,&bx,&by,&cx,&cy);len1=length(ax,ay,bx,by);len2=
(3)
len3=length(bx,by,cx,cy);printf("%f
%f %f\n",
(4)
);}(2)
float
ax,ay,bx,by,cx,cy;length(ax,ay,cx,cy);len1,len2,len3(1) #include
<math.h>练习第6章函数36程序阅读void
prn(int
a,
int
b,
int
c,
int
max,
int
min){
max=(max=a>b?a:b)>c?max:c;min=(min=a<b?a:b)<c?min:c;printf("max=%dmin);}void
main(){ int
x,
y;min=%d\n",
max,x=y=0;
prn(19,
23,
-4,
x,
y);printf("max=%d
min=%d\n",
x,
y);}输出结果为:max
=
23max
=0min
=
-4min
=
0练习第6章函数37实验指导书实验八第1、2题电子稿上传、并做在实验报告上本周实验6.3
函数调用与参数传递6.3.3
数组参数数组可以作为函数的参数使用,进行数据传送。数组用作函数参数一种是把数组元素作为实参使用;数组用作函数参数另一种是把数组名作为函数的形参和实参使用。数组元素作函数实参数数组元素作为函数的实参与普通变量作实参数并无区别,函数实参的使用与普通变量是完全相同的。在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。第6章函数38#include
<stdio.h>void
printchar(char
x);
/*声明函数原型*/void
main()第6章函数39/*调用函数*/{ int
i=0;
char
st[100];gets(st);while(st[i]!='\0'){printchar(st[i]);i++;}/*定义函数*/}void
printchar(char
x){
putchar(x);
}6.3
函数调用与参数传递数组名作为函数参数用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组(或指针变量),都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。注意:在用数组名作函数参数时,不是进行值的传送,即不是
把实参数组的每一个元素的值都赋予形参数组的各个元素。第6章函数406.3
函数调用与参数传递6.3
函数调用与参数传递因为实际上形参数组并不存在,编译系统不为形参数组分配内存。C语言中规定,数组名代表数组的首地址。因此在数组名作函数参数时所进行的传送是地址的传送,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。数据的传递成为双向的。第6章函数41一维数组参数/*声明函数原型*//*声明函数原型*/第6章函数42/*调用函数*//*调用函数*/void
get_array(int
a[]);void
print_array(int
a[]);void
main(){
int
x[10];get_array(x);print_array(x);}void
get_array(int
a[])/*定义函数实现输入数组元素功能*/{ int
i;for(i=0;i<10;i++)scanf("%d",&a[i]);}6.3
函数调用与参数传递6.3
函数调用与参数传递void
print_array(int
b[])
/*定义函数实现输出数组元素功能*/{ int
i;for(i=0;i<10;i++){ if(i
%
5==0)
printf("\n");printf("%d",b[i]);}}一维形参数组的定义形式为:类型名形参数组名[],不用指定元素个数,但一对方括号不可缺少,否则无法说明该参数为一数组。调用使用数组参数的函数时,与形参数组对应的实参是一同
类型的数组名,不需要指定元素个数,也不需要加上方括号。第6章函数43在函数中对形参数组所做改变,将被保存在形参数组所对应的存储空间中,这也就间接改变了实参数组,因为它们对应同样的存储空间。当函数调用结束后,形参数组不再对应任何存储空间,形参数组也就没有具体的元素了。但实参数组仍然对应原来的存储空间,而存储空间中改变后的值就是实参数组新的元素值。6.3
函数调用与参数传递第6章函数44用数组名作为函数参数时还应注意以下几点:形参数组和实参数组的类型必须一致,否则将引起错误。形参数组和实参数组的长度可以不相同,因为在调用时只传送首地址而不检查形参数组的长度。当形参数组的长度与实参数组不一致时,虽不至于出现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的。第6章函数456.3
函数调用与参数传递float
aver(float
a[10]){ int
i;float
av,s=a[0];for(i=1;i<10;i++)s=s+a[i];av=s/10;return
av;}void
main(){float
aver(float
a[]);float
sco[15],av;int
i;printf("input
15
scores:\n");for(i=0;i<15;i++)scanf("%f",&sco[i]);av=aver(sco);printf("average
is:
%5.2f",av);}例如:第6章函数466.3
函数调用与参数传递为了解决前述问题,通常在函数形参表中,再定义一个整型形参变量来传递数组元素的个数。即:可以把:void
aver(float a[
])写为void
aver(float
a[
],int
n)其中形参数组a没有给出长度,而由n值动态地表示数组的长度。n
的值由主调函数的实参进行传送。第6章函数476.3
函数调用与参数传递/*声明函数原型*/第6章函数48#include
<string.h>void
trans(char
s[],int
n);void
main()/*调用函数*/{ char
x[50];int
n;gets(x);n=strlen(x);puts("Before:");puts(x);putchar('\n');trans(x,n);puts("After:");puts(x);putchar('\n');}6.3
函数调用与参数传递例.
编写一个实现字符串倒置的函数。/*定义函数*/第6章函数49void
trans(char
s[],int
n){int
i;char
c;for(i=0;i<n/2;i++){c=s[i];s[i]=s[n-i-1];s[n-i-1]=c;}}6.3
函数调用与参数传递6.3
函数调用与参数传递多维数组参数调用使用多维数组参数的函数时,与形参数组对应的实参也是一个同类型的数组名,也不需要加上任何方括号。例.
编写一个函数用于查找并返回3×4的矩阵中的最大元素。int
max_value(int
a[][4],int
n);
/*声明函数原型*/void
main(){ int
x[3][4];int
m,i,j;for(i=0;i<3;i++)for(j=0;j<4;j++)scanf("%d",&x[i][j]);第6章函数50printf("%d",x[i][j]);第6章函数51for(i=0;i<3;i++){for(j=0;j<4;j++)printf("\n");}m=max_value(x,3);/*调用函数*/printf("\nThe
max
value
is
%d\n",m);}int
max_value(int
a[][4],int
n)/*定义函数*/m=a[i][j];{ intm,i,j;m=a[0][0];for(i=0;i<n;i++)for(j=0;j<4;j++)if(m<a[i][j])return(m);}6.3
函数调用与参数传递6.3.4
指针变量作参数函数的参数可以是:整型、实型、字符型等数据。C语言中函数的参数还可以是指针类型,其作用是用来传递一个变量的地址。第6章函数526.3
函数调用与参数传递例.调用swap函数来交换两个变量的值。void swap(int
*x,
int
*y){
int
temp;temp=*x;
*x=*y;
*y=temp;}void
main(
){ int
a,b,*p,*p1=&a,*p2=&b;scanf("%d%d",p1,p2);printf("\na=%d,
b=%d\n\n",
a,b);swap(p1,p2);printf("\na=%d,
b=%d\n\n",
a,b);}第6章函数536.3
函数调用与参数传递第6章函数545a&ap1p29b&b调用前5a&ap1x&a9b&bp2y&b调用时a9&ap1x&ab5&bp2y&b函数swap()执行中9a&ap1p25b&b调用结束后swap(int
*x,
int
*y){int
temp;temp=*x;*x=*y;*y=temp;}6.3
函数调用与参数传递void
swap(int
*x,
int
*y){int
*temp;*temp=*x;*x=*y;*y=*temp;}此语句不对,因指针temp变量没有明确指向第6章函数55若将swap函数写为如下:6.3
函数调用与参数传递第6章函数56若将swap函数写为如下:void
swap(int x,int
y){
int
temp;temp=x;x=y;y=temp;}void
main(){ int
a,b;scanf("%d%d",&a,&b);printf("\na=%d,
b=%d\n\n",
a,b);swap(a,b);printf("\na=%d,
b=%d\n\n",
a,b);}6.3
函数调用与参数传递第6章函数575a9b调用前x5调用时a
5y9b
9调用结束后5a9b函数swap()执行中x5ay9b59swap(int x,int
y){
int
temp;temp=x;x=y;y=temp;}6.3
函数调用与参数传递第6章函数58再如:
void swap(int
*x,
int
*y){
int
*temp;temp=x;
x=y;
y=temp;}void
main(
){ int
a,b,*p,*p1=&a,*p2=&b;scanf("%d%d",p1,p2);printf("\na=%d,
b=%d\n\n",
a,b);swap(p1,p2);printf("\na=%d,
b=%d\n\n",
a,b);}6.3
函数调用与参数传递第6章函数595a&ap1p29b&b调用前5&ap1x&a9b&bp2y&b调用时5a
a&ap1x&b9b&bp2y&a函数swap()执行中5a&ap1p29b&b调用结束后swap(int
*x,
int
*y){
int
*temp;temp=x;x=y;y=temp;}6.3
函数调用与参数传递第6章函数60说明:指针变量作形参时,对应实参为数据类型与该指针变量基类型相同的变量的地址。指针变量作形参时,参数传递的形式仍然为值传递。调用函数时,形参与对应实参占据着不同的存储空间,形参存储空间中存放的是对应实参的地址。在函数调用过程中,通过对实参变量的间接访问,改变了对应实参的值。当函数调用完毕后,形参的存储空间仍
然被收回,但此时实参存储空间中已经保留了改变后的值,从而解决了在被调用函数中改变调用函数中变量的问题。6.3
函数调用与参数传递61
第6章函数scanf("%d",&x[i][j]);for(i=0;i<3;i++)for(j=0;j<4;j++)for(i=0;i<3;i++){for(j=0;j<4;j++)printf("%d",x[i][j]);printf("\n");}m=max_value(x,3,&r,&c);printf("\nThe
max
value
is
%d\n",m);printf("\nThe
position
is
%d
row,
%d
column\n",r,c);}例.
编写一个函数用于查找3×4的矩阵中的最大元素,并返回其具体位置。int
max_value(int
a[][4],int
n,
int
*row,
int
*col);void
main(){
int
x[3][4]; int
m,i,j,r,c;6.3
函数调用与参数传递int
max_value(int
a[][4],int
n,
int
*row,
int*col){ int
m,i,j;m=a[0][0];*row=0;*col=0;for(i=0;i<n;i++)for(j=0;j<4;j++)if(m<a[i][j]){m=a[i][j];*row=i;*col=j;}return(m);}第6章函数626.3
函数调用与参数传递第6章函数63本章作业作业:教材P232
编程题第2、4题其余题目做在书上6.3
函数调用与参数传递6.3.5
函数的嵌套调用C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。虽然C语言不允许在一个函数的定义中出现对另一个函数的定义;但是C语言允许在一个函数的定义中出现对另一个函数的调用。若被调函数中需要使用另外一个函数的功能时(即被调函数中又需要调用其它函数),即出现了函数的嵌套调用。第6章函数64main(){…fa();….fb();}fa(){….fb();….}fb(){…….}6.3
函数调用与参数传递第6章函数65例.
验证哥德巴赫猜想,即一个大于等于6的偶数可以表示为两个素数之和,如6=3+3、8=3+5、10=3+7……。/*声明函数原型*//*声明函数原型*/第6章函数66#include<stdio.h>int
prime(int
n);void
guess(int
n);void
main(){ int
n,n1,n2;do{ printf("input
an
even
number(>=6):");scanf("%d",&n);}while(!(n>=6&&n%2==0));guess(n);
/*调用函数*/}6.3
函数调用与参数传递void
guess(int
n)/*定义函数*/{
int
n1,n2;for(n1=3;n1<n/2;n1+=2){ n2=n-n1;if(prime(n1)
&&
prime(n2))/*调用函数*/printf("%d=%d+%d\n",n,n1,n2);}}int
prime(int
n)/*定义函数*/{
int
i,flag
=1;for(i=2;i<=n/2;i++)第6章函数67break;
}if(n%i==0) {
flag=0;return(flag);}6.3
函数调用与参数传递6.3
函数调用与参数传递第6章函数68函数调用过程6.3
函数调用与参数传递6.3.6
函数的递归调用函数在它的函数体内直接或间接调用它自身称为递归调用。这种函数称为递归函数。
C语言允许函数的递归调用。在递归调用中,主调函数又是被调函数,执行递归函数将反复调用其自身,每调用一次就进入新的一层。例如有函数f如下:int
f
(int
x){ int
y;z=
f(y);return
z;}第6章函数696.3
函数调用与参数传递例.
用递归的方法计算n!。1
!
=
1-
2
)!-
1
)!(
n
-
1
)!
=
(
n
-
1
)
*
(
n…
…阶乘的定义:n
!
=
n
*
(
nn
*
fac(n
-1)
n
>1n
=1fac(n)
=
1第6章函数706.3
函数调用与参数传递return(f);}例.
用递归的方法计算n!。float
fact(int
n){ float
f;if(n<0)printf("n<0,error!\n");else
if(n==0||n==1)f=1;else
f=n*fact(n-1);注意,这个条件不能省第6章函数71构成递归的条件:递归结束条件及结束时的值;能用递归形式表示,并且递归向终止条件发展。6.3
函数调用与参数传递第6章函数72第6章函数73判断题:函数f可以用f(f(x))形式调用,f是递归函数。填空题函数f定义如下,计算写出f(f(4))的值是
。6int
f(int
x){int
k=1;x+=k++;
return
x;}练习错第6章函数74程序阅读题#
include<stdio.h>int
f(int
m,
int
n){ if(m%n==0)
return
n;else
return
f(n,
m%n);}void
main(){
printf("%d\n",
f(840,
48));}输出结果为:24练习第6章函数75int
f1(int,
int),
f11(int);void
f2(int);void
main(){ int
i,
j;for(i=0;
i<5;
i++){ f2((5-i)*3);for(j=0;
j<=i;
j++)
printf(“%3d”,
f1(i,
j));putchar(’\n’);}}int
f1(int
m,
int
n){ return
f11(m)/f11(n)/f11(m-n);
}int
f11(int
k){
if(k<=1) return
1;return
k*f11(k-1);
}void
f2(int
n){ int
i; for(
i=1;
i<=n;
i++)
putchar(’
’);
}11112113311
4641练习6.4
函数与指针6.4.1
返回指针值的函数所谓函数类型是指函数返回值的类型。在C语言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函数称为指针型函数。定义指针型函数的一般形式为:类型说明符
*函数名(形参表){
……
/*函数体*/}函数名前加了*号表明这是指针型函数,即返回值是指针。类型说明符表示了返回的指针值所指向的数据类型。第6章函数76例如:int
*ap(int
x,int
y){......
/*函数体*/}ap是一个返回指针值的指针型函数。返回的指针指向一个整型变量。第6章函数776.4
函数与指针第6章函数78例如:char
*day_name(int
n){
char*name[]={"
Error",
"Monday","Tuesday","Wednesday",
"Thursday",
"Friday","Saturday",
"Sunday"};return((n<1||n>7)
?
name[0]
:
name[n]);}void
main(){
int
i;printf("input
Day
No:\n");scanf("%d",&i);printf("Day
No:%2d-->%s\n“
,
i
,
day_name(i));}6.4
函数与指针6.4.2
函数的指针C语言中规定,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。函数的首地址(或称入口地址)称为函数的指针。把函数的指针赋予一个指针变量,使该指针变量指向该函数,然后通过指针变量就可以找到并调用这个函数。指向函数的指针变量称为指向函数的指针变量,也称函数指针变量。第6章函数796.4
函数与指针类型说明符(*指针变量名)();类型说明符表示被指函数的返回值的类型。(*指针变量名)表示*后面的变量是定义的指针变量。空括号表示指针变量所指的是一个函数。例如:
int
(*pf)(
);表示pf是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型。第6章函数806.4
函数与指针第6章函数81用函数。{
int
(*p)();
int
x,y,result;scanf("%d%d",&x,&y);p=add;result=(*p)(x,y);/*定义指向函数的指针变量*//*初始化指向函数的指针变量*//*
result=add(x,y);*/printf("%d+%d=%d\n",x,y,result);p=sub;result=(*p)(x,y); /*
result=sub(x,y);*/printf("%d-%d=%d\n",x,y,result);}6.4例.
通过指向函数的指针变量调intadd(inta,intb);intsub(inta,intb);voidmain()函数与指针第6章函数82int
add(int
a,int
b){return(a+b);}int
sub(int
a,int
b){return(a-b);}6.4
函数与指针第6章函数83说明:与其它指针变量相同,指向函数的指针变量在使用前也必须进行初始化操作,具体形式为“指针变量=函数名”。不要写成“指针变量=函数名(形式参数)”的形式。指向函数的指针变量可以先后指向不同的函数,但需注意函数返回值的类型与定义指针变量时所说明类型的一致性。通过指向函数的指针变量调用函数时,只需将“(*指针变量名)”代替传统调用中的函数名即可。对于指向函数的指针变量,++和--等运算是无意义的。6.4
函数与指针注意函数指针变量和指针型函数在写法和意义上的区别。int
(*p)()和int
*p()是两个完全不同的量。int(*p)()是一个变量说明,说明p是一个指向函数入口的指针变量,该函数的返回值是整型量。int*p()则不是变量说明而是函数说明,说明p是一个指针型函数,其返回值是一个指向整型量的指针,*p两边没有括号。第6章函数846.4
函数与指针第6章函数856.4.3
指向函数的指针变量作参数指向函数的指针变量的典型应用就是作为参数传递给其它函数,从而实现在函数调用过程中根据需要调用不同的函数。Sub(int
(*x1)(int),
int
(*x2)(int)){int
a,b,i,j;a=(*x1)(i);b=(*x2)(j);……}其中x1指向函数f1
,
x2指向函数f26.4
函数与指针注意:为什么不直接调用f1和f2呢?如果每次要调用的函数不同,则不需要修改sub函数,只需在调用时给不同的函数入口地址即可实现。这样做可以增加程序的灵活性,可以通过编制一个通用的程序来实现各种专用的功能。6.4
函数与指针第6章函数866.5
main函数的参数前面介绍的main函数都是不带参数的。因此main后的括号都是空括号。实际上,main函数可以带参数,这个参数可以认为是main函数的形式参数。C语言规定main函数的参数只能有两个,习惯上这两个参数写为argc和argv。因此,main函数的函数头可写为:main
(argc,argv)规定argc(第一个形参)必须是整型变量。argv(
第二个形参)必须是指向字符串的指针数组。第6章函数87加上形参说明后,main函数的函数头应写为:main
(argc,argv)int
argv; char
*argv[];或写成:
main
(int
argc,char
*argv[])如命令行“rename
a.cpp
b.cpp”argc=3renamea.cppb.cppargv[0]argv[1]argv[2]argv6.5
main函数的参数第6章函数88第6章函数89本周实验实验指导书实验八第3、5题电子稿上传,并做在实验报告上6.6
函数与变量的作用域6.6.1
局部变量与全局变量变量的有效性范围称变量的作用域。在讨论函数的形参变量时曾经提到,形参变量只在被调用期间才分配内存单元,调用结束立即释放。这一点表明形参变量只有在函数内才是有效的,离开该函数就不能再使用了。这种变量有效性的范围称变量的作用域。C语言中所有的量都有自己的作用域。
变量说明的方式不同,其作用域也不同。第6章函数906.6
函数与变量的作用域intcalculator(void);void
main(){ char
op; int
a,b,result;scanf("%d%c%d",&a,&op,&b);result=calculator();printf("%d%c%d=%d\n",a,op,b,result);}intcalculator(void){
switch(op)第6章函数91{casecasecase'+':result=a+b;break;'-':result=a-b;break;'*':result=a*b;break;case'/':if(b!=0)result=a/b;else
printf("error!");}return(result);}第6章函数92编译程序,出现以下出错信息:错误:未定义的符号‘op’在calculator
函数中错误:未定义的符号'result'在calculator
函数中错误:未定义的符号'a'在calculator
函数中错误:未定义的符号'b'在calculator
函数中若在calculator函数中添加上这四个变量的定义,结果如何呢?6.6
函数与变量的作用域局部变量局部变量也称为内部变量。局部变量是在函数内作定义说明的。局部变量的作用域仅限于函数内,离开该函数后再使用这种变量是非法的。第6章函数936.6
函数与变量的作用域第6章函数94例如:int
f1(int
a)/*函数f1*/{ int
b,c;……}
a,b,c作用域int
f2(int
x)
/*函数f2*/{
int
y,z;}main()x,y,z作用域/*主函数main*/{
int
m,n;}
m,n
作用域6.6
函数与变量的作用域intcalculator(inta,charop,intb);voidmain()第6章函数95}{ char
op; int
a,b,result;/*result作用域的开始*/scanf("%d%c%d",&a,&op,&b);result=calculator(a,op,b);printf("%d%c%d=%d\n",a,op,b,result);/*result作用域的结束*/int
calculator(int
a,
char
op,
int
b){ int
result;
/*result作用域的开始*/switch(op){
case
'+':result=a+b;break;case
'-':result=a-b;break;case
'*':result=a*b;break;case
'/':if(b!=0)result=a/b;else
printf("error!");
}return(result);
/*result作用域的结束*/}6.6
函数与变量的作用域关于局部变量作用域的说明:主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。第6章函数966.6
函数与变量的作用域在一个函数内部,还可以在复合语句中定义变量,其作用域只局限于该复合语句void
main()第6章函数97/*a、b作用域的开始*/{ int
a,b;scanf(“%d%d”,&a,&b);if(a<b)/*t作用域的开始*/{ int
t;t=a;a=b;b=t;}}/*t作用域的结束*//*a、b作用域的结束*/6.6
函数与变量的作用域全局变量全局变量也称为外部变量,它是在函数外部定义的变量。全局变量不属于哪一个函数,它属于一个源程序文件。
全局变量的作用域是从定义的位置开始到本源程序文件的结束。全局变量的说明符为extern。在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。第6章函数986.6
函数与变量的作用域第6章函数99例如:int
a,b;void
f1()/*全局变量*//*函数f1*/{
……}float
x,y;int
fz()/*全局变量*//*函数fz*//*主函数*/{
……}main(){
……}
/*全局变量x,y作用域全局变量a,b作用域*/6.6
函数与变量的作用域int
max_value(int
a[][4],int
n);introw,col;
/*定义全局变量,row、col作用域开始*/void
main(){ int
x[3][4];int
m,i,j;for(i=0;i<3;i++)for(j=0;j<4;j++)scanf("%d",&x[i][j]);for(i=0;i<3;i++)第6章函数100{for(j=0;j<4;j++)printf("%d",x[i][j]);printf("\n");}m=max_value(x,3);printf("\nThe
max
valueis
%d\n",m);printf("%d,%d\n",row,col);/*使用全局变量row、col*/}6.6
函数与变量的作用域intmax_value(inta[][4],intn){intm,i,j;第6章函数101/*使用全局变量row、col*/m=a[0][0];row=0;col=0;for(i=0;i<n;i++)for(j=0;j<4;j++)if(m<a[i][j])/*使用全局变量row、col*/{
m=a[i][j];row=i;col=j;}return(m);}/*全局变量row、col作用域结束*/6.6
函数与变量的作用域对于全局变量的说明:全局变量在其定义位置之后的所有函数中均可直接使用。全局变量可以作为函数间数据联系的渠道,使得函数之间的数据联系不只局限于参数传递和return语句,通过全局变量可以从函数中得到一个以上的返回值。在全局变量的作用域中,所有函数均可直接访问全局变量,也就是说如果在一个函数中改变了全局变量的值,就能影响
到其它函数。这使得程序容易出错,难以判断出错原因。非必要时不要使用全局变量,因为全局变量在全部程序执行时占用存储单元并且降低了程序的通用性和清晰性。第6章函数1026.6
函数与变量的作用域函数设计原则(“黑箱”观点)所有的输入是以参数的形式传递给函数的所有的输出是以函数值的形式返回函数中使用的变量是局部变量,与主调函数没有任何关系6.6
函数与变量的作用域第6章函数103关于变量同名局部变量同名不同函数中定义的局部变量(包括形参)可以同名,由于其任用域不同,因此互不影响同一函数中不能定义两个同名的局部变量(包括形参)全局变量与局部变量同名当全局变量与某个函数中的局部变量同名时,在该函数内部局部变量的作用域将“覆盖”同名全局变量的作用域第6章函数1046.6
函数与变量的作用域void
first(void);void
second(void);char
color='B';第6章函数105/*定义全局变量*/void
main(){ printf("Color
in
main()
is
%c\n",color);first();printf("Color
in
main()
is
%c\n",color);second();printf("Color
in
main()
is
%c\n",color);}void
first(void){ char
color
='R';
/*定义局部变量*/printf("Color
in
first()
is
%c\n",color);}void
second(void){
color='G';printf("Color
in
second()
is
%c\n",color)}6.6
函数与变量的作用域程序输出如下:Color
in
main()
is
BColor
in
first()
is
R第6章函数106Colorinmain()
is
BColorColorininsecond()
ismain()
is
GG6.6
函数与变量的作用域全局变量同名同一文件中的全局变量不允许同名,不同文件中的全局变量可以同名,但是当把这些文件组成一个程序时(有关方法参见6.2节),将会造成命名冲突。若要在不同文件中使用同一个全局变量,应当采用以下方法:首先,在任一个文件中定义全局变量;然后,在所有需要使用此全局变量的文件中使用extern对该变量进行声明。extern起到将全局变量的作用域扩展到其它文件的作用。第6章函数1076.6
函数与变量的作用域第6章函数108int
a,b;/*外部变量定义*/char
c;
/*外部变量定义*/main(){……}F2.Cextern
int
a,b;extern
char
c;func
(int
x,y){……}例如有一个源程序由源文件
F1.C和F2.C组成:F1.C/*外部变量说明*//*外部变量说明*/6.6
函数与变量的作用域6.6.2
动态存储变量与静态存储变量各种变量作用域不同,就本质来说是变量的存储类型不同。存储类型是指变量占用内存空间的方式,也称存储方式。
变量的存储方式可分为“静态存储”和“动态存储”两种。静态存储变量通常是在变量定义时就分定存储单元并一直保持不变,直至整个程序结束。动态存储变量是在程序执行过程中,使用它时才分配存储单元,使用完毕立即释放。第6章函数1096.6
函数与变量的作用域动态存储变量系统在程序运行期间根据需要对存储空间进行动态分配的变量称为动态存储变量。在函数中定义的局部变量和形式参数都属于动态存储变量。动态存储变量的值在函数调用结束后就无法使用了,即变
量的生存期并不等于程序的整个执行期。每次调用函数时,系统会为这些变量重新分配存储空间,
这意味着若在一个程序中两次调用同一个函数,系统分配
给这个函数中的局部变量和形参的存储空间可能是不同的。可以在函数内部定义局部变量时使用auto关键字,从而显式声明该变量采用动态存储方式。注意,不能在声明形参时使用auto关键字。6.6
函数与变量的作用域第6章函数110静态存储变量系统在程序运行期间为其分配固定存储空间的变量称为静态存储变量。全局变量就是一种静态存储变量,系统在程序开始执行时为其分配存储空间,直到程序执行完毕才释放,因此整个程序执行期都是全局变量的生存期。可以使用static关键字将局部变量的存储方式声明为静态存储方式,在函数调用结束后保留其局部变量的值,即不释放局部变量所占据的存储空间,从而在下一次调用该函数时可能继续使用上一次调用结束时的结果。6.6
函数与变量的作用域第6章函数111自动变量和寄存器变量属于动态存储方式,外部变量和静态变量属于静态存储方式。变量的说明不仅应说明其数据类型,还应说明存储类型。变量说明的完整形式为:存储类型说明符
数据类型说明符
变量名,变量名…;例如:
static
int
a,b;auto
char
c1,c2;extern
int
x,y;第6章函数112说明a,b为静态类型变量说明c1,c2为自动字符变量(省略)说明x,y为外部整型变量C语言中的存储类型auto-自动变量
extern-外部变量register-寄存器变量
static-静态变量6.6
函数与变量的作用域自动变量类型autoauto存储类型是C语言程序中使用最广泛的一种类型。
C语言规定,函数内凡未加存储类型说明的变量
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年湖北省孝感市公开招聘警务辅助人员辅警笔试自考题1卷含答案
- 2024年宠物医院项目投资申请报告代可行性研究报告
- 2023-2024学年度七年级地理第一学期期末质量检测卷(解析版)
- 2024版教育教学人员聘用合同样本版B版
- 2024淘宝店铺品牌形象设计及电商导购合同3篇
- 2024版家庭装饰装修细节施工合同版B版
- 2024版企业借款协议详细范例版B版
- 2024版产品外观设计授权协议3篇
- 2024版医院体检协议书范本
- 2022年-2023年高校教师资格证之高等教育心理学模拟考试试卷A卷(含答案)
- 医疗机构资产负债表(通用模板)
- 废旧锂离子电池高值资源化回收利用项目环评报告书
- 审计英语词汇大全讲课教案
- JIS G3507-1-2021 冷镦用碳素钢.第1部分:线材
- 初二家长会ppt通用PPT课件
- 小学生家庭作业布置存在的误区及改进策略论文1
- 一元一次含参不等式教学设计83
- 生物医学研究的统计学方法课后习题答案 2014 主编 方积乾
- 牛仔面料成本核算
- 加拿大矿业政策
- 客情关系的建立和维护
评论
0/150
提交评论