第八章 函 数
重点:
了解模块化程序设计的思想熟悉掌握函数的使用了解变量的作用范围
[例1:] 函数调用
main()
{ printf("This is a test!\n");
printstar();
print_message();
printstar();
}
printstar()
{
printf(“* * * * * * * * * * * * \n”);
}
print_message()
{
printf(,How do you do!\n”);
}
8.1函数(function)介绍说明:
1.? 一个源程序文件有一个或多个函数组成。
2.? 一个C程序可以有多个源程序文件组成。
3.? 任何C程序都从main函数开始执行,当main的最后一条指令执行完,程序结束.
4.? 所有函数都是独立的,可以互相调用,main不能被调用。
5.? 函数分为标准函数和用户自定义函数。
6.? 函数分为无参函数和有参函数。
为什么要include 文件?
printf();scanf(),putchar();getchar();puts();gets()都在stdio.h中。
8.2 函数定义及函数声明
1.函数定义
(1),无参数函数的定义
类型标识符 函数名( )
{ 声明部分语句
}
(2),有参数函数的定义
类型标识符 函数名(形式参数表列 )
{声明部分语句
}
形式参数表列:对形式参数的说明;有两种方法:
① int max(int x,int y)
{
函数体;
}
② int max(x,y)
int x,y;
{
函数体;
}
注意:在①中x,y 的数据类型一致,也不能写成int max(int x,y)的型式。
(3) 可以有空函数
void 函数名( 参数 )
2.函数参数及函数的值形参和实参形参:在函数定义时,函数名括号后面的变量名。
实参:在函数被子调用是,函数名括号后面的表达式。
(2) 调用函数和被调用函数
(3)函数的值。
例2,求一个整数的绝对值
int absolute_value(int x)
{ return(X>0=? X,-X);
}
main()
{ int x,y;
printf(“please input a int x=”);
scanf(“%d”,&x);
y=value(x);
printf(“\n %d absolute is %d\n”,x,y);
}
(4)说明:
① 实际参数可以是变量、常量、表达式,它们的值传给形式参数。实参对形参传递的是值;
② 形参在未调用时,并没有分配到内存单元,只有被子调用时才分配内存单元,调用完后释放内存单元。
③实参与形参的个数、类型要匹配。
④实参与形参是自左向右一一配对的。
⑤形参在函数中被改变并不影响实参的值。
例3:float max(float x,float y,float z)
{float t;
t=x>y? x,y;
t=t>z?t,z;
return(t);
}
main()
{ float x,y,z,maxvale;
printf(“please input a x,y,z,”);
scanf(“%f%f%f”,&x,&y,&z);
maxvale=max(x,y,z);
printf(“\n the max is %f\n”,max);
}
3.函数的返回值若要从函数中返回一个值,在函数中要用return语句;
格式:return ( 表达式);
return;
程序执行到return语句时就退出被调用的程序,返回到被调用的语句处。
例,void spc(int n)
{ int I;
for ( I=0;I<n;I++)
printf(“,);
return;}
例4,解下述表达式
x*x-x+1 (x<0)
y=
x*x*x+x+3 (x>=0)
float fx(float x)
{ if (x<0)
return(x*x-x+1);
else
return(x*x*x+x+3);
}
main()
{ float x,y;
printf(“please input a x,”);
scanf(“%f”,&x);
y=fx(x);
printf(“\n whten x is %f”,x);
printf(“\n the y is %f”,y);
}
4.对被调用函数的说明一个函数中调用另一函数需具备的条件:
①被调用的函数必须存在。
②如果使用库函数,还应用#include命令调用头文件;
如果用户自定义的函数,一般还应在主调函数中对被调函数的返回值的类型说明。型式为:
类型标识符 被调用函数名( );
例5:
main()
{ float max( ); /*对被调函数的说明,也可表示为float*/ /*max(float x,float y,float z)及float*/ /*max(float,float,float )*/
float x,y,z;
printf(“please input a x,y,z,”);
scanf(“%f%f%f”,&x,&y,&z);
printf(“\n the max is %f\n”,max(x,y,z));
}
float max(float x,float y,float z)
{float t;
t=x>y? x,y;
t=t>z?t,z;
return(t);
}
以下几种情况可以不在调函数前对被调用函数作类型说明。
①如果数的值(函数返回的值)是整型或字符型,系统对它们自动按整型说明。
②如果被调用函数的定义出现在主调函数之前,可以不必加以说明。
③如果已在所有函数定义之前,在文件的开头,在函数的外部已说明了函数类型,则在各调用函数中不用再说明。
8.3函数调用
8.3.1 函数调用的一般形式
1.函数名(实参表列)
实参表列的实参与实参之间用逗号隔开,实参可为表达式,实参的个数要与形参一一对应。
2.函数在程序中的位置。
①作为一般语句。 Printf();
②作为一个表达式的一部份。C=2*max(a,b);
③作为另一个函数的参数。 M=max(a,max(b,c));
8.3.2 函数的嵌套调用在一个函数中调用一函数,在被调函数中又调用另一函数。
函数中调用其它函数叫函数的嵌套。
C中函数的定义是平行的,除了main以外,都可以互相调用。
函数不可以嵌套定义。

8.3.3 函数的递归调用函数自己调用自己,即函数的递归调用,递归调用可以使程序简洁、代码紧凑,但要牺牲内存空间作处理时的堆栈。
要点:在函数中要有终止递归调用的if语句。
例6:如要求一个n!(n的阶乘)的值可用下面递归调用:
1 n=1
f(n)=
n*f(n-1) n≠1
#include,stdio.h”
main()
{ long mul( int n);
int m;
printf("Calculate n! n=?\n");
scanf("%d",&m); /*键盘输入数据*/
printf("%d!=%ld\n",m,mul(m));/*调用子程序计算并输出*/
getch();
}
long mul( int n)
{
long p;
if(n>1)
p=n*mul(n-1); /*递归调用计算n!*/
else
p=1;
return(p); /*返回结果*/

8.4 数组作为函数参数数组元素可以做函数实参,其用法与变量相同。数组名也可以做实参和形参,传递的是整个数组。
1.数组元素做函数实参。
数组元素作函数的实参,与用变量做实参一样,是单向传递,即“值传送”方式。
例7.比较两个数组的对应元素,比较过程在函数中完成,计较结果在主函数中输出.
main()
{ int a[10],b[10],i,n=0,m=0,k=0;
printf(“输入数组 a:\n");
for (i=0;i<10;i++)
scanf("%d",&a[i]);
printf(“输入数组b:\n");
for (i=0;i<10;i++)
scanf("%d",&b[i]);
for (i=0;i<10;i++)
{ if(large(a[i],b[i])==1)n++;
else
if(large(a[i],b[i])==0)m++;
else k++;
}
printf(“a>b%d次\na=b%d次\na<b%d次\n",n,m,k);
}
large(int x,int y)
{ int flag;
if(x>y) flag=1;
else if(x<y) flag=-1
else flag=0;
return(flag);
}
1 3 5 7 9 8 6 4 2 0
2 3 6 9 -1 -3 5 6 0 4
a>b 4 次
a=b 1 次
a<b 5 次
2.可用数组名作函数参数,此时实参与形参都应用数组名(或用数组指针)。
注意:
数组参数不是单向传输,形参的改变会导致实参的改变。
数组名做参数时,函数中得到的数组的地址,而不是象变量那样单独有一个存储空间,因此主函数和子函数是对同一个数组名进行操作
例8:有一个一维函组score,内放10个学生成绩,求平均成绩。
#include<stdio.h>
float average ( float array[10]);
main()
{ float score[10],aver;
int i;
printf("input 10 scores:\n");
forIi=0;I<10;I++)
scanf("%f",&score[I]);
printf("%\n");
aver=average(scor);
printf(average score is %5.2f",aver);
}
float average ( float array[10]);
{ int i;
float aver,sum=array[0];
for(i=1;i<10;i++)
sum=sum+array[i];
aver=sum/10;
return(aver);
}
8.5变量性质及存储属性
8.5.1变量性质变量可以在程序中三个地方说明,函数内部、函数的参数定义中或所有的函数外部。根据所定义位置的不同,变量可分为局部变量、形式参数和全程变量。
1.局部变量
在函数内部定义的变量,只有在函数内才能引用,而函数外不能使用这些变量。函数的形式参数也是属于局部变量。
例9:#include<stdio.h>
void f(void);
main()
{ int x;
x=10;
printf(“x in main() is %d\n”,x);
}
f();
printf(“x in main() is still %d \n”,x);
}
void f(void)
{int x;
x=100;
printf(“the x in f() is %d\n”,x);
}
说明:不同函数定义的变量名可以相同,它们分配的内存单元不同,是不同意义的变量。
2.全局变量全局变量是指在所有函数之外说明的变量,它在整个程序内部者是"可见的",可以被任何一个函数使用,并且在整个程序的运行中都保留其值。习惯上通常在程序的主函数前说明。
例10,
#include <stdio.h>
int test; /*定义全程变量*/
void f1(int x,float y); /*子函数说明*/
void f2(void); /*子函数说明*/
main()
{
test=5; /*给全程变量赋值*/
f1(20,5.5); /*调用有形式参数的子函数f1()*/
/*test的值变成115*/
f2(); /*调用f2(),test的值变为1150*/
}
void f1(int x,float y)
{
float z; /*z定义为局部变量*/
z=x*y; /*计算*/
test=test+z;
}
void f2(void)
{
int count=10; /*定义局部变量并初始化*/
test=test*count;
}
例11,
int a=3,b=5;
main()
{void swap(void);
printf(" a=%d,b=%d\n",a,b);
swap( );
printf(" a=%d,b=%d\n",a,b);
}
void swap()
{ int t;
t=a;a=b;b=t;
}
8.5.2 变量的存储属性
Turbo C2.0支持四种变量存储类型。说明符如下,
auto static extern register
1、auto
auto称为自动变量,自动变量是在函数执行时分配存储单元,函数执行完后释放其占有存储单元。在编译时,自动变量是一个不确定的值。
格式:auto int b,c=3;
auto 可以省略。
2、static
static称为静态变量。静态变量在未初始化赋值时,有一个确定的值(int 型值为0,float 型值为0.0,char 型 为’\0’).根据变量的类型可以分为静态局部变量和静态全程变量。
(1),静态局部变量
它与局部变量的区别在于,在函数退出时,这个变量始终存在,但不能被其它函数使用,当再次进入该函数时,将保存上次的结果。其它与局部变量一样。
int fac(n)
int n;
{static int f=1;
f=f*n;
return(f);
}
main()
{int i;
for(i=1;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
(2),静态全程变量
静态全程变量就是指只在定义它的源文件中可见而在其它源文件中不可见的变量。它与全程变量的区别是,全程变量可以再说明为外部变量(extern),被其它源文件使用,而静态全程变量却不能再被说明为外部的,即只能被所在的源文件使用。
3、extern
extern称为外部变量。为了使全局变量除了在定义它的源文件中可以使用外,还要被其它文件使用。因此,必须将全程变量通知每一个程序模块文件,此时可用 extern来说明。
例如,
文件1为file1.c 文件2为file2.c
int i,j;/*定义全程变量*/ extern int i,j;/*说明将i,j从 文件1中复制过来*/
char c; extern char c; /*将c复制过来*/
void func1(int k); func2() /*用户定义函数*/
{
main() static float k;/*定义静态变量*/
{ i=j*5/100;
func1(20);/*调用函数*/ k=i/1.5;
func2();,
,,
,,
,}
}
func1(int k) /*用户定义函数*/
{
j=k*100;
}
对于以上两个文件file1.c和file2.c,用Turbo C2.0的集成开发环境进行编译连接时,首先应建立一个.prj的文件。例如file.prj,该文件内容如下,
file1.c
file2.c
然后将file.prj的文件名写入主菜单Project中的Project Name项中。 再用F9编译连接,就可产生一个文件名为fioe.exe的可执行文件。
4、register
register称为寄存器变量。它只能用于整型和字符型变量。只允许同时定义两个寄存器变量,一旦超过两个,编译程序会自动地将超过限制数目的寄存器变量当作非寄存器变量(auto)来处理。因此,寄存器变量常用在同一变量名频繁出现的地方。
定义一个整型寄存器变量可写成,
register int a;
例:下列程序的结果是多少?
main()
{int n,i,c;
printf(“\n input n:”);
scanf(“%d”,&n);
for(i=1;i<=n;i++)
c=a( i );
printf(“%d”,c);
}
int a(int x)
{ static int a=1;
a=a*x;
return(a);
}
例:写出对n个数组元素进行起泡排序、选择排序的函数。
void sort(array,n)
int array[];
int n;
{int i,j,k,t;
for(i=0;i<n-1;i++)
{ k=i;
for (j=i+1;j<n;j++)
if (array[j]<array[k]) k=j;
t=array[k];array[k]=array[i];array[i]=t;}
}
main()
{int a[10],i;
printf("enter the array\n");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
printf("the sorted array:\n");
for (i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
本章小结函数的定义、调用、声明、参数、返回值
*函数的嵌套调用和递归调用
*数组名作为函数参数的传递
*局部变量和全局变量的作用域
*局部变量和全局变量的存储类别
*函数的作用域和存储类别
*多文件处理