函数程序设计(一)
内容
说明
主要内容
重点
难点
1.函数的概念、定义格式
2.函数的调用方法
3.函数的形式参数、实际参数和函数的返回值
结合例子讲解函数的分类
重点是用户自定义函数
1.函数的定义格式
2.函数的调用方法
3.函数的返回值
1.函数的定义格式
2.函数的调用方法
一、问题的引入
在程序设计中,常将一些常用的功能模块编写成函数,放在函数库中供公共选用,以减少重复编写程序段的工作量。
一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能。所有高级语言中都有子程序这个概念,用子程序实现模块的功能。在C语言中,子程序的作用是由函数完成的。一个C程序可由一个主函数和若干个函数构成,由主函数调用其它函数,其它函数也可以相互调用。同一个函数可以被一个或多个函数调用任意多次。
二、函数的概念定义:函数是一个可反复使用的程序段,其它的程序段均可通过调用语句来执行这段程序。
三、函数的分类
1.从用户使用的角度分
(1)系统函数:即库函数,由系统提供,用户可直接使用。
(2)用户自定义函数:为满足特定需要用户自己定义的函数。
2.从函数的形式分
(1)有参函数:被调用时需接受主调函数传递来的数据。
(2)无参函数:被调用时不需要主调函数传递数据。
四、函数的定义
(1)有参函数的定义格式:
存储类型说明符 数据类型说明符 函数名(形式参数表)
形式参数说明序列;
{ 数据定义语句序列;
执行语句序列;
}
(2)无参函数的定义格式:
存储类型说明符 数据类型说明符 函数名()
{ 数据定义语句序列;
执行语句序列;
}
举例说明
说明:
①存储类型说明符可以是extern或static两种。extern定义的函数叫做外部函数,可被其它编译单位中的函数调用;static定义的函数称内部函数,只能被本编译单位中的函数调用。该项可省略,默认为外部函数。
②数据类型说明符
规定本函数返回值的数据类型。可是前面介绍的各种基本数据类型,也可是指针型或“void”型,表示本函数无返回值。
③函数名:
是一个标识符,在同一编译单位中函数不能重名。
④形式参数表每个形式参数可是一个变量名、数组名、指针变量名、指针数组名等。
⑤形式参数说明序列
若干条形式参数的说明语句,数据类型相同的形式参数可用一条说明语句说明。语句格式为:数据类型 形式参数1,…;
⑥数据定义语句序列
由本函数中使用的变量、数组、指针变量等的定义语句组成。
⑦执行语句序列由本函数中完成函数功能的程序段组成。在本序列中可以有一条返回语句“return(表达式);”,返回语句【return】的功能是结束本函数的运行,返回到主调函数的调用语句后继续执行。其中表达式的值就是本函数的返回值。
任何函数都有返回值,且只能有一个。当函数没有指明返回值,即“return;”或没有返回语句时,函数执行后实际上不是没有返回值,而是返回一个不确定的值,有可能给程序带来某种意外的影响。因此,为了保证函数不返回任何值,C语言规定,可以定义无类型函数,其形式为:
void 函数名(形参表)
{ …… }
【例】编写一个函数,求2个整型数的最大值并返回这个最大值。
程序清单:
main()
{ int a,b,c;
scanf(“%d,%d”,&a,&b);
c=max(a,b);
printf(“max=%d”,c);
}
int max(int x,int y)
  { int z;
z=x>y?x∶y;
return(z);

五、函数的调用主调函数通过传递一定的信息来使用被调函数的功能。
1.无返回值的函数调用格式函数名(实际参数表);
此格式作为语句形式调用,最后有“;”。
有返回值的函数调用格式
变量名=函数名(实际参数表);
函数的调用过程
(1)为所有的形参分配内存,计算各个实际参数表达式的值,依次赋予对应的形式参数。(若是“无参函数”,上述工作不执行)
(2)进入函数体,执行函数中的语句,实现函数的功能,当执行到“返回语句”时,计算返回值,释放本函数体中的变量等(静态型变量不释放),收回分配给形参的内存,返回主调函数。
(3)继续执行主调函数中的后继语句。
4.函数调用时对被调函数的说明方法
(1)调用系统函数时,除少数系统函数(如scanf、printf等)外,都要求在程序的开始用包含命令“#include<头文件.h>”将定义系统函数的库文件包含在本程序中。
(2)如果被调函数和主调函数在一个源程序文件中,在书写顺序上被调函数在主调函数之前出现;或者被调函数虽然在主调函数之后出现,而被调函数的数据类型是整型或字符型,可不对被调函数加以说明;除上述2种情况为,都要对被调函数加以说明。
说明的位置一般在主调函数的函数体开头的数据说明语句部分。
格式:数据类型 被调函数名( );
(3)如果被调的用户函数和主调函数不在同一个源程序文件中,则在定义函数的源程序文件中用下列方式将函数定义成外部函数:
extern 数据类型 函数名(形式参数表)
同时在主调函数的函数体中,或所在源程序文件的开头将要调用的函数说明成“外部函数“。
格式,extern 数据类型 被调函数名( );
【例】根据三边长求出三角形的面积。
#include <stdio.h>
#include <math.h>
float sabc(float a,float b,float c)
{ float hl,s; 
hl=0.5*(a+b+c); 
s= sqrt(hl*(hl-a)*(hl-b)*(hl-c))
return (s); 
}
main( )
{ float x,y,z,area;
scanf(“%f,%f,%f”,&x,&y,&z);
if ((x+y>z)&&(y+z>x)&&(z+x>y))
printf(″该三角形面积等于%5.2f \n″,sabc(x,y,z));  else
printf(″不能构成三角形!\n″);
}
六、小结函数定义、调用方法;return语句;
被调函数出现在主调函数之前,不必说明。
函数程序设计(二)
内容
说明
主要内容重点难点
函数调用中的数据传递方法
结合例子讲解值传递方式
函数调用中的数据传递方法
函数调用中的数据传递方法
一、函数调用中的数据传递方式函数间传递数据的方式有:值传递;地址传递;返回值传递;全局变量传递。前2种利用函数的参数来传递数据,后2种不是利用函数参数传递数据。
1.值传递方式
① 是在形式参数和实际参数之间传递数据
② 所传递的是参数值调用函数时,将实际参数的值求出赋予对应的形式参数。在函数体中对形参的处理与实参无关。当函数体执行完毕,形参的值可能发生变化,但返回后,形参的值不带回到对应的实参中。
③ 特点:参数值的单向传递
④ 形参一般是变量;实参可是变量或表达式。
【例】 调用函数时的数值传递。
#include<stdio.h>
void swap(int x,int y);
main ( )
{ int a,b;
a=10; b=20; /* 说明两个变量并赋初值 */
printf ("before swap,a=%d,b=%d\n",a,b);
swap(a,b); /* 用变量a和b作为实际参数调用函数 */
printf ("after swap a=%d,b=%d\n",a,b);
}
void swap (int x,int y)
{ int temp;
/* 借助临时变量temp交换两个形参变量x和y的值 */
temp = x;
x = y;
y = temp;
printf ("in swap x=%d,y=%d\n",x,y);
}
在调用swap时其各个参数和变量的状态和相互关系可用下图描述。

在程序中调用swap时,实参变量a和b的值传递给了形参变量x和y,并且在函数的内部完成了x和y值的交换,但是x和y与a和b各自使用自己的内存区域,它们之间仅仅在参数传递时进行了数值的传递,所以变量x和y的变化并不影响变量a和b。
程序的实际运行结果为:
before swap a=10,b=20
in swap x=20,y=10
after swap a=10,b=20
如何在被调用函数中改变调用函数中变量的值,这就要使用指针变量作为参数,这一问题将在后面详细讨论。
2.地址传递方式
①是在形式参数和实际参数之间传递数据
②所传递的是地址调用函数时,将实际参数的地址赋予对应的形式参数作为其地址。由于形参和实参的地址相同(占用相同的内存),所以调用时,可看成将实参的值传递给形参;返回时,可看成将形参的值回带给对应的实参。
③特点:参数值的双向传递
④形参是数组名或指针变量;实参是变量的地址、数组名或指针变量
【例】判断一个整数数组中各元素的值,若大于0,则输出该值;若小于或等于0,则输出0。
void func1(int a[ ],int n)
{
int i;
printf(“\n数组a的值是:\n”);
for(i=0;i<n;i++)
{ if(a[i]<=0) a[i]=0;
printf(“%4d”,a[i]);
}
}
main( )
{ int b[5],i;
printf(“\n请输入5个数:\n”);
for(i=0;i<5;i++)
scanf(“%d”,&b[i]);
printf(“\n数组b的初值是:\n”);
for(i=0;i<5;i++)
printf(“%4d”,b[i]);
func1(b,5);
printf(“\n数组b的终值是:\n”);
for(i=0;i<5;i++)
printf(“%4d”,b[i]);
}
举例说明
【例】 若在主函数中变量a=5,b=10,编写一个函数交换主函数中两个变量的值,使变量a=10,b=5。(指针也可以作为函数的参数,它的作用是将一个变量的地址传递到另一个函数中。)
#include<stdio.h>
main ( )
{ void swap(int *x,int *y);
int a,b,*p,*q;
a=5; b=10; /* 说明两个变量并赋初值 */
p=&a;q=&b;
printf ("交换前:a=%d,b=%d\n",a,b);
swap(p,q); /* 用变量a和b作为实际参数调用函数 */
printf ("交换后:a=%d,b=%d\n",a,b);
}
void swap ( int *x,int *y)
{ int temp; /* 借助临时变量交换两个形参变量x和y的值 */
temp = *x;
*x =*y;
*y = temp;
}
程序输出如下:
交换前:a=5 b=10
交换后:a=10 b=5
swap( )函数中使用两个指针变量x和y作为形参。程序运行时,先执行main( )函数中的给a和b赋值语句,使a的值为5,b的值为10,并将a和b的地址分别赋给p和q,见下图(a)。main( )函数调用swap( )函数时,将实参的值传送给形参变量。虚实结合后,x和p指向的是同一存储单元,y和q指向的是同一存储单元,见下图(b)。接着执行swap( )函数体,使*x和*y的值交换,也就是a和b的值的交换,见下图(c)。函数调用结束后,x和y被释放,情况如下图(d)。最后在main( )函数中输出的a和b的值是经过交换的值。

运用指针变量作参数,可以在被调函数中改变主调函数中定义的变量的值。
结合例子讲解地址传递方式
3.值传递和地址传递方式的区别
①值传递:传递的是数值
②地址传递:传递的是变量地址。
二、小结调用函数时,数据的传递主要是通过形参与实参相结合来实现的,被调用者需从调用处获得多少数据,就应该定义多少个形参。
在函数调用时,实参的个数及其顺序必须与形参的个数及其顺序完全一致,其类型必须与形参的类型相同或赋值兼容。
三、思考与练习从键盘输入10个浮点数,求出其和及平均值。要求编写求和及求平均值的函数。
重点强调函数使用中应注意的问题
函数程序设计(三)
内容
说明
主要内容
重点
难点
函数的嵌套调用函数的递归调用简介
函数的嵌套调用
函数的嵌套调用
一、函数的嵌套调用
在调用一个函数的过程中,又调用另一个函数。
【例】求两个数的最小公倍数(函数的嵌套调用方法)。
#include<stdio.h>
gbs(int x,int y)
{ int z,f;
z=gys(x,y);
f=x*y/z;
return f;
}
gys(int a,int b)
{ int r;
r=a%b;
while(r!=0)
{a=b;b=r;r=a%b;}
return b;
}
main()
{ int m,n,p;
printf("input m,n");
scanf("%d,%d",&m,&n);
p=gbs(m,n);
printf("%d\n",p);
}
此程序中,函数main()调用了gbs()、gbs()调用函数gys(),实现了嵌套调用。
C语言规定:不限制嵌套调用的层数,嵌套调用的层数仅受计算机内存的限制。
二、函数的递归调用
1.定义,在调用一个函数的过程中又出现直接或间接地调用该函数本身。
如图所示:
2.递归过程必须解决两个问题递归计算的公式递归结束的条件
3.递归过程的算法描述:
if (递归结束条件)return 递归结束条件下的返回值);
else return (递归计算公式);
4.递归调用函数的调用方法和一般函数的调用方法完全相同
三、小结递归定义、调用方法、执行过程、递归的算法。
四、思考与练习
写几个函数:a、输入10个职工的姓名和职工号 b、按职工号有小到大顺序排序,姓名顺序也随之调整 c、要求输入一个职工号,用折半查找的方法找出该职工的姓名,从主函数输入要查询的职工号,输出该职工的姓名。
函数程序设计(四)
内容
说明
主要内容
重点难点
1、变量的作用域
2、变量的存储类别
3、内部函数和外部函数
结合例子讲解局部变量的作用域
变量的作用域
变量的作用域
一、变量的作用域、局部变量和全局变量
1、变量的作用域规则变量只能在它的作用范围内使用,变量的作用域与定义变量的位置有关。
2、局部变量在一个函数内部(或复合语句内部)定义的变量是内部变量,它只在本函数范围内有效。
①函数不能使用其他函数中定义的变量
②不同函数中可使用相同名字的变量,它们代表不同的对象,互不干扰。
③形式参数也是局部变量函数内部,在复合语句中定义的变量只在本复合语句中有效。
局部变量也称为内部变量。局部变量是在函数内定义的。其作用域仅限于本函数内,其它函数是不能访问这些变量的。
例如:
int func1(int a) /*函数func1*/
{ int b,c;
……}
int func2(int x) /*函数func2*/
{ int y,z;
……}
main() /*主函数main*/
{ int m,n;
func1(m);
func2(n);
}
在函数func1( )内定义了三个变量,a为形参,b和c为一般变量。在 func1( )的范围内a、b、c有效,或者说a、b、c变量的作用域限于func1( )内。同理,x、y、z的作用域限于func2( )内。 m、n的作用域限于main( )函数内。
关于局部变量的作用域还要说明以下几点:
①主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平等关系。
② 形参也属于局部变量。
③ 允许在不同的函数中使用相同的变量名,它们代表不同的变量,分配不同的存储单元,互不干扰,也不会发生混淆。
④ 在复合语句中定义的变量,其作用域只在复合语句范围内。
【例】分析下面程序:
main()
{ int i=2,j=3,k;
k=i+j;
if(i= =2)
{ int k=8;
i=3;
printf("%d ",k);
}
printf("%d %d\n",i,k);
}
输出结果为:8 3 5
本程序在main( )中定义了i、j、k三个变量,其中k未赋初值。 而在复合语句内又定义了一个变量k,并赋初值为8。应该注意这两个k不是同一个变量。在复合语句外由main( )定义的k起作用,而在复合语句内则由在复合语句内定义的k起作用。因此程序第3行的k为main( )所定义,其值应为5。第7行输出k值,该行在复合语句内,由复合语句内定义的k起作用,其初值为8,故输出值为8,第9行输出i,k值。i是在整个程序中有效的,第6行对i赋值为3,故输出也为3。而第9行已在复合语句之外,输出的k应为main所定义的k,此k值由第3 行已获得值为5,故输出也为5。
3、全局变量:
在函数之外定义的变量称外部变量,外部变量是全局变量(全程变量)。它的有效范围为从定义变量的位置开始到本源文件结束。
【例】输入正方体的长、宽、高:l、w、h。求体积及三个面的面积。
int s1,s2,s3;
int vs( int a,int b,int c)
{ int v;
v=a*b*c;
s1=a*b;
s2=b*c;
s3=a*c;
return(v);
}
main()
{ int v,l,w,h;
printf("\n 请输入长方体的长、宽、高,\n");
scanf("%d%d%d",&l,&w,&h);
v=vs(l,w,h);
printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3);
}
运行结果为:
请输入长方体的长、宽、高:
4 5 6 ↙
v=120 s1=20 s2= 30 s3=24
在本例中,如不使用全局变量,在主函数中就不可能取得s1、s2、s3三个值。而本程序中定义了三个全局变量s1、s2、s3,用来存放三个面积,其作用域为整个程序。函数vs( )用来求正方体体积和三个面积,函数的返回值为体积v,在函数vs中求得的s1、s2、s3值在main( ) 中仍然有效。由主函数完成长、宽、高的输入及结果输出。由于C语言规定函数返回值只有一个,当需要多个函数的返回数据时,用全局变量是一种很好的方式。因此全局变量是实现函数之间数据通讯的有效手段。
4、对于全局变量还有以下几点说明:
⑴ 在一个函数内不仅可以使用本函数定义的局部变量,还可以使用在此函数之前定义的全局变量。
⑵ 全局变量可以作为一种数据传递的渠道,使函数间的数据传递变得十分方便,并且通过全局变量还可以使函数返回多个数据。不过,过多地使用全局变量会降低函数的独立性和通用性,因此建议应尽量少使用全局变量。
⑶ 如果在一个文件中有与全局变量同名的局部变量,那么在局部变量起作用的范围内,全局变量不起作用。
二、变量的存储类别变量的生存期是指变量值保留的期限,可分为两种情况:
⑴ 静态存储:变量存储在内存中的静态存储区,在编译时就分配了存储空间,在整个程序运行期间,该变量占有固定的存储单元,程序结束后,这部分空间才释放。这类变量的生存期为整个程序。
⑵ 动态存储:变量存储在内存中的动态存储区,在程序运行过程中,只有当变量所在函数被调用时,系统才为该变量分配内存单元,函数调用结束,这部分空间释放。这类变量的生存期仅在函数调用期间。
局部变量也称为自动变量,全局变量又称为外部变量。
三、内部函数和外部函数
1.内部函数在定义函数时,如果在其函数类型名前另加“static”,则所定义的函数为内部函数(或称局部函数、静态函数)。内部函数的特点是只能被所在源文件中的函数调用。因此,不同源文件中的内部函数,即使同名也互不干扰。
2.外部函数在定义函数时,如果在函数类型名前另加“extern”(注意:extern可缺省),则所定义的是外部函数(或称全局函数)。外部函数不仅能被所在源文件的函数调用,还可以被其它源文件中的函数调用,但在其它源文件中调用该函数时,应用extern说明该函数为外部函数。由于函数都是外部性质的,因此,在定义函数时,关键字extern可以省略。
结合例子讲解全局变量的作用域
四、小结
C语言中的变量有全局变量和局部变量之分。全局变量的作用域是从它定义的位置开始到源文件的结束,局部变量的作用域仅局限于定义它的函数。变量的生存期是指变量值保留的期限,可分为静态存储和动态存储,静态存储是指变量存储在内存中的静态存储区,在编译时就分配了存储空间,这类变量的生存期为整个程序,动态存储是指变量存储在内存中的动态存储区,这类变量的生存期仅在函数调用期间。
自定义函数分为内部函数和外部函数,内部函数与主函数在同一源文件中,外部函数在不同的源文件中。外部函数不仅能被所在源文件的函数调用,还可以被其它源文件中的函数调用,但在其它源文件中调用该函数时,应用extern说明该函数为外部函数。
五、思考与练习
1、写一个判断素数的函数,在主函数输入一个整数,输出是否是素数的信息。
2、写一个函数,使输入的一个字符串按反序存放,在主函数中输入和输出字符串。
3、写一个函数,输入以行字符,将此字符串中最长的单词输出
重点强调变量的作用域