第七章
本章要点
函数的概念
函数的定义与调用
函数的递归调用
变量的作用域
函数的作用域
主要内容
7.1 函数概述
7.2函数定义的一般形式
7.3函数参数和函数的值
7.4函数的调用
7.5函数的嵌套调用
主要内容
7.6 函数的递归调用
7.7 数组作为函数参数
7.8 变量的作用域 --局部变量和全局变量
7.9 变量的存储方式和生存期
7.10 关于变量的声明和定义
7.11 内部函数和外部函数
7.1 函数概述一个C程序可由一个主函数和若干个其他函数构成。一个较大的程序可分为若干个程序模块,
每一个模块用来实现一个特定的功能。在高级语言中用子程序实现模块的功能。子程序由函数来完成。
函数间的调用关系,由主函数调用其他函数,
其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。
例 7.1函数调用的简单例子
# include <stdio.h>
void main()
{
void printstar(); /*对 printstar函数声明 */
void print_message(); /*对 print_message函数声明 */
printstar(); / *调用 printstar函数 */
print_message(); /*调用 print_message函数 */
printstar(); / *调用 printstar函数 */
}
void printstar() / *定义 printstar函数 */
{
printf("* * * * * * * * * * * * * * * *\n");
}
void print_message() / *定义 print_message函数 */
{
printf("How do you do!\n");
}
运行情况如下:
* * * * * * * * * * * * * * * *
How do you do!
* * * * * * * * * * * * * * * *
说明:
1.一个C程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件 。 对于较大的程序,通常将程序内容分别放在若干个源文件中,
再由若干源程序文件组成一个 C程序 。 这样便于分别编写,分别编译,提高调试效率 。 一个源程序文件可以为多个 C程序公用 。
说明:
2,一个源程序文件由一个或多个函数以及其他有关内容 ( 如命令行,数据定义等 ) 组成 。
一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的 。
说明:
3,C程序的执行是从 main函数开始的,如果在
main函数中调用其他函数,在调用后流程返回到
main函数,在 main函数中结束整个程序的运行 。
说明:
4,所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的 。 一个函数并不从属于另一函数,即函数不能嵌套定义 。 函数间可以互相调用,但不能调用 main函数 。
main函数是系统调用的 。
说明:
5.从用户使用的角度看,函数有两种:
① 标准函数,即库函数 。 这是由系统提供的,用户不必自己定义这些函数,可以直接使用它们 。 不同的 C系统提供的库函数的数量和功能会有一些不同,但许多基本的函数是共同的 。
② 用户自己定义的函数 。 用以解决用户的专门需要 。
说明:
6,从函数的形式看,函数分两类:
① 无参函数 。 无参函数一般用来执行指定的一组操作 。 在调用无参函数时,主调函数不向被调用函数传递数据 。
② 有参函数 。 主调函数在调用被调用函数时,通过参数向被调用函数传递数据 。
7.2 函数定义的一般形式
7.2.1,为什么要定义函数
C语言规定,在程序中用到的所有函数,必须,先定义,后使用,。 定义函数包括以下几个内容:
⒈ 指定函数的名字,以便以后按名调用 。
⒉ 指定函数的类型,函数值的类型 。
⒊ 指定函数的参数的名字和类型,以便在调用函数时向它们传递数据 。 对无参函数不需要这项 。
⒋ 指定函数应当执行什么操作,也就是函数是做什么的,即函数的功能 。 这是最重要的 。
7.2 函数定义的一般形式
7.2.1,为什么要定义函数对于 C编译系统提供的库函数,是由编译系统事先定义好的,程序设计者不必自已定义,只需 #include
命令把有关的头文件包含到本文件模块中即可。
例如,在程序中若用到数学函数 (如 sqrt,fabs,sin,cos
等 ),就必须在本文件模块的开头写上:
#include <math.h>
7.2 函数定义的一般形式
7.2.2 怎样定义无参函数定义无参函数的一般形式为,
类型标识符 函数名 ()

声明部分语句部分

7.2 函数定义的一般形式
7.2.3 怎样定义有参函数定义有参函数的一般形式为,
类型标识符 函数名 (形式参数表列)

声明部分语句部分

例如:
int max( int x,int y)
{int z; / *函数体中的声明部分 */
z=x>y?x ∶ y;
return(z);

7.2 函数定义的一般形式
7.2.4 定义空函数定义空函数的一般形式为,
类型标识符 函数名 ()
{ }
例如:
void dummy( )
{ }
主调函数调用空函数时,只表明这里要调用一个函数,但函数本身什么工作也不做等,以后扩充函数功能时补充上。
7.3 函数参数和函数的值
7.3,1形式参数和实际参数形式参数,函数名后面括弧中的变量名称为
,形式参数,(简称,形参,) ;
实际参数,主调函数中调用一个函数时,函数名后面括弧中的参数 (可以是一个表达式 )
称为,实际参数,(简称,实参,) ;
函数返回值,return后面的括弧中的值作为函数带回的值(称 函数返回值 )。
主调函数和被调用函数之间有数据传递的关系。在不同的函数之间传递数据,
可以使用的方法有:
◆ 参数:通过形式参数和实际参数
◆ 返回值:用 return语句返回计算结果
◆ 全局变量:外部变量例 7.2输入两个整数,要求用一个函数求出其中的大者,并在主函数中输出此值 。
#include <stdio.h>
void main ( )
{ int max(int x,int y );
/* 对max函数的声明 */
int a,b,c;
scanf( " % d,% d",&a,&b ) ;
c=max ( a,b ) ;
printf( "Max is % d",c ) ;

int max(int x,int y )/ *定义有参函数 max */

int z;
z=x>y? x ∶ y;
return( z ) ;

运行情况如下:
7,8 ↙
Max is 8
通过函数调用,可使两个函数中的数据发生联系。
关于形参与实参的说明:
1,在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数 max中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。
2,实参可以是常量、变量或表达式,
例如:
max(3,a+b);
但要求它们有确定的值。在调用时将实参的值赋给形参。
3.在被定义的函数中,必须指定形参的类型。
4.实参与形参的类型应相同或赋值兼容。
5,实参向形参的数据传递是单向,值传递”,只能由实参传给形参,而不能由形参传回来给实参。在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。
7.3 函数参数和函数的值
7.3.2 函数的返回值函数的返回值 是通过函数调用使主调函数得到的确定值。
例如,例 8.2中,max(2,3)的值是3,max(5,
2)的值是 5。赋值语句将这个函数值赋给变量c。
说明:
1.函数的返回值是通过函数中的 return语句获得的。
一个函数中可以有一个以上的 return语句,执行到哪一个 return语句,哪一个语句起作用。
return语句后面的括弧也可以不要例如,,return z ;” 等价于,return ( z ) ;,
return后面的值可以是一个表达式 。
例如,max ( int x,int y )
{ return( x >y? x,y ) ; }
2,函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。
例如,下面是 3个函数的首行:
int max( float x,float y ) /* 函数值为整型 */
char letter( char c1,char c2) /* 函数值为字符型 */
double min( int x,int y ) /* 函数值为双精度型 */
注意:
凡不加类型说明的函数,自动按整型处理。
3.在定义函数时指定的函数类型一般应该和 return语句中的表达式类型一致。
如果函数值的类型和 return语句中表达式的值不一致,
则以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。
4.对于不带回值的函数,应当用,void” 定义函数为
“无类型”(或称“空类型”)。此时在函数体中不得出现 return语句。
例 7.3 返回值类型与函数类型不同
# include <stdio.h>
void main()
{ int max( float x,float y) ;
float a,b;
int c;
scanf("%f,%f,",&a,&b);
c=max(a,b);
printf("Max is %d\n",c);

int max( float x,float y)
{ float z; /* z为实型变量 */
z=x>y?x ∶ y;
return(z);

运行情况如下:
1.5,2.5 ↙
Max is 2
7.4 函数的调用
7.4.1 函数调用的一般形式函数调用的一般形式为,函数名 (实参表列)
说明,
1.如果是调用无参函数,则,实参表列,可以没有,
但括弧不能省略。
2.如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应匹配。实参与形参按顺序对应,向形参传递数据。
3,如果实参表列包括多个实参,对实参求值的顺序并不是确定的,有的系统按自左至右顺序求实参的值,有的系统则按自右至左顺序 。 例 如:
printf ( ″% d,% d ″,i,++i ) ;
若i的原值为3,在 VC++ 6.0环境下运行的结果不是,3,4‖,而为,4,4‖。 因为按自右而左顺序,先求
++i得 4,再向左进行,此时的 i已是 4了 。
如果想输出 3和 4,应写成:
i=3;
j=i++;
printf(”%d,%d”,i,j);
7.4 函数的调用
7.4.2 函数调用的方式
1.函数语句把函数调用作为一个语句。这时不要求函数带回值,
只要求函数完成一定的操作。
2.函数表达式函数出现在一个表达式中,这种表达式称为 函数表达式 。这时要求函数带回一个确定的值以参加表达式的运算。例如,c=2 *max(a,b);
按函数在程序中出现的位置来分,可以有以下三种函数调用方式:
3.函数参数函数调用作为一个函数的实参。
例如,m = max (a,max ( b,c ) ) ;
其中 max ( b,c )是一次函数调用,它的值作为
max另一次调用的实参。 m的值是 a,b,c三者中的最大者。
7.4 函数的调用
7.4.3 对被调用函数的声明和函数原型
1.首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一条件还不够。
3.如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面,应该在主调函数中对被调用的函数作声明。
2.如果使用库函数,还应该在本文件开头用 #include
命令将调用有关库函数时所需用到的信息“包含”到本文件中来。
例 7.4 对被调用的函数作声明
# include <stdio.h>
void main()
{ float add( float x,float y);
/ *对被调用函数 add的声明 */
float a,b,c;
scanf("% f,% f",& a,& b);
c= add( a,b) ;
printf(" sum is % f \n",c);

float add( float x,float y) / *函数首部 */
{ float z; /* 函数体 */
z=x+y;
return( z);

运行情况如下:
3.6,6.5↙
Sum is 10.100000
函数原型的一般形式为,
1,函数类型 函数名 (参数类型 1,参数类型 2) ;
2,函数类型 函数名 (参数类型 1,参数名 1,参数类型 2,参数名 2) ;
声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。
注意:
函数的,定义,和,声明,不是一回事。
函数的定义是指对 函数功能的确立,包括指定函数名,函数值类型、形参及其类型,函数体 等,它是一个完整的、独立的函数单位。
函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。它不包含函数体 。
说明:
( 1)如果被调用函数的定义出现在主调函数之前,可以不必加以声明。
(2) 如果已在文件的开头 (在所有函数之前 ),
已对本文件中所调用的函数进行了声明,则在各函数中不必对其所调用的函数再作声明 。
例 7.5(例 7.4的改写)
# include <stdio.h>
float add( float x,float y) / *函数首部 */
{ float z;
z=x+y;
return( z);

void main()
{ /*不必再对 add函数作声明 */
float a,b,c;
scanf("% f,% f",& a,& b);
c= add( a,b) ;
printf(" sum is % f \n",c);

7.5 函数的嵌套调用嵌套定义就是在定义一个函数时,其函数体内又包含另一个函数的完整定义 。
在主函数中调用一个 max_4函数来求 4个整数中的最大数。然后在 max_4函数中再调用一个 max_2函数来求 2个整数中的最大数。
最后在主函数中输出结果。
方法:
例 7.6 输入 4个整数,找出其中最大的数 。
用函数的嵌套调用来处理 。
#include <stdio.h>
void main()
{
int max_4(int a,int b,int c,int d); /* max_4函数的声明 */
int a,b,c,d,max;
printf("Please enter 4 interger numbers:");
scanf("%d %d %d %d ",&a,&b,&c,&d);
max=max_4(a,b,c,d); /* 调用 max_4函数 */
printf("max=%d \n",max);
}
int max_4(int a,int b,int c,int d) /* max_4函数的定义 */
{int max_2(int,int); /* max_2函数的声明 */
int m;
m=max_2(a,b); /* 调用 max_2函数 */
m=max_2(m,c); /* 调用 max_2函数 */
m=max_2(m,d); /* 调用 max_2函数 */
return(m); /* 函数返回值是 4个数中的最大者 */
}
int max_2(a,b) /* max_4函数的定义 */
int max_2(a,b)
{if(a>b)
return a;
else
return b; /* 函数返回值是 a和 b中的大者 */
}
运行情况如下:
Please enter 4 interger numbers:11 45 –54 0↙
max=45
7.6 函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用 。 C语言的特点之一就在于允许函数的递归调用 。
例如:
int f ( int x )
{ int y,z;
z=f ( y ) ; /* 在执行 f函数的过程中又要调用 f函数 */
return( 2 *z ) ;

例 7.7,有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3
个人大2岁。问第3个人,又说比第2个人大2岁。
问第2个人,说比第1个人大2岁。最后问第1个人,
他说是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
可以用数学公式表述如下:
age( n) = 10 (n=1)
age( n-1) +2 (n >1)
可以用一个函数来描述上述递归过程:
int age( int n) / * */
{ int c; / * c用作存放函数的返回值的变量 */
if(n==1) c=10;
else c=age(n-1)+2;
return(c);

运行结果如下:
18
用一个主函数调用 age函数,求得第 5人的年龄 。
#include <stdio.h>
void main( )
{ printf( ″% d ″,age( 5 )) ;

例 7.8 用递归方法求n!
求n!也可以用递归方法,即5!等于4! × 5,
而4!=3! × 4? 1!=1。
可用下面的递归公式表示:
n! ︳ =1 (n=0,1)
︳ = n · (n-1)! (n>1)
程序如下:
#include <stdio.h>
void main()
{long fac(int n); /* 对 fac函数的声明 */
int n;
long y;
printf("input an integer number:");
scanf("%d",&n);
y=fac(n);
printf("%d!=%ld\n",n,y);
}
long fac(int n) /* 定义 fac函数 */
{
long f;
if(n<0)
printf("n<0,data error!");
else if(n==0||n==1)
f=1;
else f=fac(n-1)*n;
return(f);
}
运行情况如下:
input an integer nunber
,10 ↙
10!=3628800
例 7.9 Hanoi( 汉诺塔 ) 问题,
由上面的分析可知:将n个盘子从A座移到C
座可以分解为以下 3个步骤:
1.将A上n-1个盘借助C座先移到B座上。
2.把A座上剩下的一个盘移到C座上。
3.将n-1个盘从B座借助于A座移到C座上。
程序如下:
#include <stdio.h>
void main()
{
void hanoi(int n,char one,char two,char three);
/* 对 hanoi函数的声明 */
int m;
printf("input the number of diskes:");
scanf(―%d‖,&m);
printf("The step to moveing %d diskes:\n",m);
hanoi(m,'A','B','C');
}
void hanoi(int n,char one,char two,char three)
/* 定义 hanoi函数,将n个盘从 one座借助 two座,移到
three座 */
{
void move(char x,char y); /* 对 move函数的声明 */
if(n==1) move(one,three);
else
{ hanoi(n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three); }
}
void move(char x,char y) /* 定义 move函数 */
{
printf(―%c-->%c\n",x,y);
}
运行情况如下:
input the number of diskes:3↙
The steps to noving 3 diskes:
A-->C
A-->B
C-->B
A-->C
B-->A
B-->C
A-->C
7.7 数组作为函数参数
7.7.1 数组元素作函数实参由于实参可以是表达式,而数组元素可以是表达式的组成部分,因此数组元素可以作为函数的实参,
与用变量作实参一样,是单向传递,即,值传送,
方式 。
例 7.10
有两个数组a和b,各有10个元素,将它们对应地逐个相比 ( 即a [ 0 ] 与b [ 0 ] 比,a [ 1 ]
与b [ 1 ] 比 ) 。 如果a数组中的元素大于b
数组中的相应元素的数目多于 b数组中元素大于 a数组中相应元素的数目 (例如,a[ i] >b[ i] 6次,b
[ i] >a[ i] 3次,其中 i每次为不同的值 ),则认为
a数组大于 b数组,并分别统计出两个数组相应元素大于,等于,小于的次数 。
#include <stdio.h>
void main()
{ int large(int x,int y); /* 函数声明 */
int a [10],b [10],i,n=0,m=0,k=0;
printf( ″enter array a∶ \n ″);
for(i=0;i<10;i++= )
scanf( ″%d ″,&a[i]);
printf( ″\n ″);
printf( ″ enter arrayb ∶ \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;
else if( large (a [i],b [i] )==0) m =m +1;
else k=k+1; }
printf("a[i]>b[i] %d times\na[i]=b[i] %d
times\na[i]<b[i] %d times\n",n,m,k);
if(n>k) printf("array a is larger than array b\ n");
else if (n<k) printf("array a is smaller than array b\n");
else printf("array a is equal to array b\ n");

large( int x,int y)
{ int flag;
if(x>y)flag=1;
else if(x<y) flag=-1;
else flag=0;
return( flag);

运行情况如下:
enter array a:
1 3 5 7 9 8 6 4 2 0 ↙
enter array b ∶
5 3 8 9 –1 –3 5 6 0 4↙
a[i]>b[i] 4 times
a[i]=b[i] 1 times
a[i]<b[i] 5 times
array a is smaller thann array b
7.7 数组作为函数参数
7.7.2 数组名作函数参数可以用数组名作函数参数,此时形参应当用数组名或用指针变量 。
例 7.11
有一个一维数组 score,内放 10个学生成绩,求平均成绩。
#include <stdio.h>
void main()
{ float average( float array[ 10]) ; /* 函数声明 */
float score[10],aver;
int i;
printf( ″input 10 scores:\n ″);
for(i=0;i<10;i++=
scanf( ″%f ″,& score[i]);
printf( ″\n ″);
aver= average( score );
printf ( ″ average score is %5,2f \n″,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);
}运行情况如下:
input 10 scores:
100 56 78 98,5 76 87 99 67,5 7
5 97 ↙
average score is 83.40
例 7.12 编写一个函数,用来分别求数组 score_1和数组 score_2各元素的平均值。
#include <stdio.h>
void main()
{ float average( float array[ ],int n)
float score_1[5] = {98.5,97,9
1,5,60,55 };
float score_2[ 10] ={ 67.5,89.5,99,69,5,
77,89.5,76.5,54,60,99.5};
printf(―the average of class A is %6.2f\ n‖,
average(score_1,5));
printf(―the average of class B is %6.2f\ n‖,
average(score_2,10));

float average( float array[ ],int n)
{ int i;
float aver,sum=array[0];
for(i=1;i<n;i++=
sum= sum+ array[i];
aver= sum/n;
return(aver);

运行结果如下:
the average of class A is 80.40
The average of class B is 78.20
例 7.13
用选择法对数组中 10个整数按由小到大排序。所谓选择法就是先将 10个数中最小的数与 a[ 0]对换 ;再将 a[ 1]到 a[ 9]中最小的数与 a[ 1]对换 每比较一轮,找出一个未经排序的数中最小的一个。共比较 9轮。
未排序时的情况,
a[0] a[1] a[2] a[3] a[4]
3 6 1 9 4
将 5个数中最小的数 1与 a[ 0]对换,
1 6 3 9 4
将余下的 4个数中最小的数 3与 a[ 1]对换
1 3 6 9 4
将余下的 3个数中最小的数 4与 a[ 2]对换
1 3 4 9 6
将余下的 2个数中最小的数 6与 a[ 3]对换,至此完成排序
1 3 4 6 9
程序实例
#include <stdio.h>
void main()
{ void sort( int array[],int n) ;
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 ″);

void sort( 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 ])
{t =array[k];
array[k]=array[i];array[i]=t;}

} 运行结果如下:enter array:
5 7 –3 21 –43 67 321 33 51 0↙
The sorted array:
–43 –3 0 5 7 21 33 51 67 321
7.7 数组作为函数参数
7.7.3 用多维数组名作函数参数多维数组元素可以作函数参数,这点与用一维数组元 素作函数参数的情况类似 。
也可以用多维数组名作为函数的实参和形参,在被调用函数中对形参数组定义时可以指定每一维的大小,也可以省略第一维的大小说明 。
例 7.14
有一个3 × 4的二维数组,设计一个函数,用来求二维数组中所有元素中的最大值。
程序如下:
#include <stdio.h>
void main()
{ max_value ( int array[ ][ 4] );
int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};
printf( ″max value is %d\n ″,
max_value(a) );

算法:先使变量 max的初值为二维数组中第一个元素的值,然后将二维数组中各个元素的值与 max相比,每次比较后都把“大者”存放在
max中,全部元素比较完后,max 的值就是所有元素的最大值。
max_value ( int array[ ][ 4])
{ int i,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);
} 运行结果如下:
Max value is 34
7.8 变量的作用域 --局部变量和全局变量
7.8.1 局部变量在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在此函数以外是不能使用这些变量的。这称为“局部变量”。
float f1( int a) /* 函数 f1 */
{int b,c;
… a,b,c有效
}
char f2(int x,int y) /* 函数 f2 */
{int i,j; x,y,i,j有效
}
void main( ) /* */
{int m,n;
… m,n有效
}
(1)主函数中定义的变量只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。
(2) 不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。
(3) 形式参数也是局部变量。
(4) 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为
“分程序”或“程序块”。
void main ( )
{int a,b;

{int c;
c=a+b; c在此范围内有效 a,b在此范围内有效

}

}
7.8 变量的作用域 --局部变量和全局变量
7.8.2 全局变量在函数内定义的变量是局部变量,而在函数之外定义的变量称为外部变量,外部变量是全局变量 (也称全程变量 )。全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。
int p=1,q=5; /* 外部变量 */
float f1(int a) /* 定义函数 f1 */
{int b,c;

}
char c1,c2; /* */
char f2 (int x,int y) /* 定义函数 f2 */
{int i,j; 全局变量 p,q的作用范围
… 全局变量 c1,c2的作用范围
}
void main ( ) /* */
{int m,n;

}
例 7.15 有一个一维数组,内放10个学生成绩
,写一个函数,求出平均分,最高分和最低分 。
#include <stdio.h>
float Max=0,Min=0; / * */
void main()
{ float average( float array[ ],int n) ;
float ave,score[10];
int i;
for(i=0;i<10;i++)
scanf( ″%f ″,&score[i]);
ave= average(score,10);
printf(―max=%6.2f\ nmin=%6.2f\ n
average=%6.2f\ n―,Max,Min,ave);
}
float average( float array[ ],int n)
/ * 定义函数,形参为数组 */
{ int i;
float aver,sum=array[0];
Max=Min=array[0];
for(i =1;i<n;i++)
{ if( array[i]> Max) Max= array[i];
else if( array[i]< Min) Min= array[i];
sum=sum+array[i];

aver=sum/n;
return(aver);

运行情况如下:
99 45 78 97 100 67.5 8
9 92 66 43 ↙
max=100.00
min=43.00
average=77.65
建议不在必要时不要使用全局变量,原因如下:
① 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。
② 使用全局变量过多,会降低程序的清晰性。在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。
③ 它降低函数的通用性,因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去。
但若该外部变量与其他文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性。一般要求把C程序中的函数做成一个封闭体,除了可以通过
“实参 ——形参”的渠道与外界发生联系外,没有其他渠道。
例 7.1 6 外部变量与局部变量同名
#include <stdio.h>
int a=3,b=5; /* a,b为外部变量 */ a,b作用范围
void main ( )
{ int a=8; /*a为局部变量 */ 局部变量 a作用范围
printf (″%d″,max (a,b)); 全局变量 b的作用范围
}
max (int a,int b) /*a,b为局部变量 */
{ int c;
c=a> b?a∶ b; 形参 a,b作用范围
return (c);
}
运行结果为 8
7.9 变量的存储方式和生存期从变量的 作用域 (即从空间)角度来分,可以分为全局变量和局部变量。
从变量值的 生存期 (即存在的时间)角度来分,
又可以分为静态存储方式和动态存储方式。
7.9 变量的存储方式和生存期所谓静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。这个存储空间可以分为三部分:
1.程序区 2.静态存储区 3.动态存储区在C语言中每一个变量和函数有两个属性:
数据类型 和 数据的存储类别 。
7.9 变量的存储方式和生存期
7.9.1 局部变量的存储类别
1,auto变量,函数中的局部变量,如不专门声明 static
存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量 (包括在复合语句中定义的变量 ),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。
例如:
int f( int a) / *定义 f函数,a为形参 */
{ auto int b,c=3; /*定义b、c为自动变量 */

2,静态局部变量,当函数中的局部变量的值在函数调用结束后不消失而保留原值时,该变量称为静态局部变量。用关键字 static进行声明。
7.9 变量的存储方式和生存期
7.9.1 局部变量的存储类别例 7.1 7 考察静态局部变量的值
#include <stdio.h>
void main()
{ int f( int) ;
int a=2,i;
for(i=0;i<3;i++=
printf( ″%d ″,f(a));

int f( int a)
{auto int b=0;
static c=3;
b=b+1;
c=c+1;
return(a+b+c);

对静态局部变量的说明:
1,静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释放。
2,对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。
3.如在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)
或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。
4,虽然静态局部变量在函数调用结束后仍然存在,
但其他函数不能引用它。
例 7.1 8 输出1到5的阶乘值
#include <stdio.h>
void main()
{ int fac( int n) ;
int i;
for(i=1;i<=5;i++)
printf( ″%d! =%d\n ″,i,fac(i));

Int fac( int n)
{ static int f=1;
f=f *n;
return(f);

3,寄存器变量 ( register),变量的值是存放在内存中的。当程序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。
经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。
7.9 变量的存储方式和生存期
7.9.1 局部变量的存储类别如果有一些变量使用频繁,则为存取变量的值要花费不少时间。为提高执行效率,C语言允许将局部变量的值放在 CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。
由于对寄存器的存取速度远高于对内存的存取速度,
因此这样做可以提高执行效率。这种变量叫做 寄存器变量,用关键字 register作声明。
7.9 变量的存储方式和生存期
7.9.1 局部变量的存储类别说明,三种局部变量的存储位置是不同的:自动变量存储在动态存储区;静态局部变量存储在静态存储区;
寄存器存储在 CPU中的寄存器中 。
7.9 变量的存储方式和生存期
7.9.2 全局变量的存储类别全局变量都是存放在静态存储区中的。因此它们的生存期是固定的,存在于程序的整个运行过程。
一般来说,外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。但有时程序设计人员希望能扩展外部变量的作用域。 方法有以下几种:
1,在一个文件内扩展外部变量的作用域例 7.19调用函数,求 3个整数中的大者。
#include <stdio.h>
void main()
{int max( ); /* 函数声明 */
extern int A,B,C; /* 外部变量声明 */
printf("Please enter three integer numbers:");
scanf("%d %d %d",&A,&B,&C); /* 输入外部变量的值 */
printf("max is %d\n",max());
}
1,在一个文件内扩展外部变量的作用域
int A,B,C; /* 定义外部变量 */
int max( )
{int m;
m=A>B?A:B;
if (C>m) m=C;
return(m);
}
运行结果为:
Please enter three integer numbers:34 –21 78↙
max is 78
2.将外部变量的作用域扩展到其他文件
#include <stdio.h>
int A; /*定义外部变量 */
void main()
{ int power( int); /*函数声明 */
int b=3,c,d,m;
printf( ″enter the number a and its power m:\n″);
scanf( ″%d,%d ″,& A,&m);
c= A*b;
printf( ″%d *%d=%d\n ″,A,b,c);
d=power(m);
printf( ″%d **%d=%d \n″,A,m,d);

例 7.20 用 extern将外部变量的作用域扩展到其他文件 。 本程序的作用是给定b的值,输入a和m,求a × b和 am的值
。 文件 file1,c中的内容为:
文件 file2.c中的内容为:
extern A; /*声明 A为一个已定义的外部变量 */
int power( int n) ;
{ int i,y=1;
for(i=1;i<=n;i++)
y *= A;
return(y);

3,将外部变量的作用域限制在本文件中在程序设计中,某些外部变量只限于被本文件引用,
而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。
例如:
file1.c file2.c
static int A; extern int A;
void main ( ) void fun (int n)
{ {…
… A=A*n;
}
7.9.2 全局变量的存储类别用 static
(1) 对局部变量用 static声明,把它分配在静态存储区
,该变量在整个程序执行期间不释放,其所分配的空间始终存在 。
(2) 对全局变量用 static声明,则该变量的作用域只限于本文件模块 (即被声明的文件中 )。
注意,用 auto,register,static声明变量时,是在定义变量的基础上加上这些关键字,不能单独使用。下面用法不对:
int a; /* 先定义整型变量 a */
static a; /* 再对变量 a声明为静态变量 */
7.9 变量的存储方式和生存期
7.9.3 存储类别小结
(1)从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:
局部变量包括:
自动变量,静态局部变量、寄存器变量。
形式参数可以定义为自动变量或寄存器变量
全局变量包括:
静态外部变量、外部变量
( 2)从变量存在的时间来区分,有动态存储和静态存储两种类型。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。
动态存储,自动变量,寄存器变量,形式参数
静态存储:态局部变量,静态外部变量,外部变量
(3) 从变量值存放的位置来区分,可分为:
内存中静态存储区:静态局部变量、静态外部变量、
外部变量
内存中动态存储区:自动变量和形式参数
CPU中的寄存器:寄存器变量
(4) static对局部变量和全局变量的作用不同。对局部变量来说,它使变量由动态存储方式改变为静态存储方式。而对全局变量来说,它使变量局部化,但仍为静态存储方式。从作用域角度看,凡有 static声明的,其作用域都是局限的,或者是局限于本函数内,或者局限于本文件内。
7.10 关于变量的声明和定义对变量而言,声明与定义的关系稍微复杂一些。
在声明部分出现的变量有两种情况:一种是需要建立存储空间的 (如,int a; ),另一种是不需要建立存储空间的(如,extern a;)。前者称为,定义性声明,(defining declaration),或简称 定义 ( definition)。
后者称为,引用性声明,(referencing declaration)。广义地说,声明包括定义,但并非所有的声明都是定义。
对,int a;‖ 而言,它既是声明,又是定义。而对
,extern a;‖ 而言,它是声明而不是定义。
一般为了叙述方便,把 建立存储空间的声明称定义,而 把不需要建立存储空间的声明称为声明 。显然这里指的声明是狭义的,即非定义性声明。例如:
void main()
{extern A; /*是声明不是定义。声明 A是一个已定义的外部变量 */

}
int A;
7.11 内部函数和外部函数根据函数能否被其他源文件调用,将函数区分为 内部函数 和 外部函数 。
7.11.1内部函数如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加 static。即
static 类型标识符 函数名 (形参表 )
例如,static int fun ( int a,int b )
7.11.2 外部函数
(1) 定义函数时,如果在函数首部的最左端加关键字
extern,则表示此函数是外部函数,可供其他文件调用。例如,函数首部可以写为 extern int fun (int
a,int b),这样,函数 fun就可以为其他文件调用。
如果在定义函数时省略 extern,则隐含为外部函数。
(2) 在需要调用此函数的文件中,用 extern对函数作声明,表示该函数是在其他文件中定义的外部函数例 7.21有一个字符串,内有若干个字符,今输入一个字符,要求程序将字符串中该字符删去。用外部函数实现
File.c(文件1)
#include <stdio.h>
void main()
{ extern void enter_string(char str[]);
extern void detele_string(char str[],char ch);
extern void print_string(char str[]);/ *以上 3行声明在本函数中将要调用的在其他文件中定义的 3个函数 */
char c;
char str[80];
scanf("%c",&c);
detele_string(str,c);
print_string(str); }
file2.c(文件2)
#include <stdio.h>
void enter_string(char str[80]) / * 定义外部函数
enter-string*/
{ gets(str); / *向字符数组输入字符串 */
}
file3.c(文件3)
void delete_string(char str[],char ch) / *定义外部函数
delete_string */
{ int i,j;
for(i=j=0;str[i]!='\0';i++)
if(str[i]!=ch)
str[j++]=str[i];
str[i]='\0';
}
file4.c(文件4)
#include <stdio.h>
void print_string(char str[])
{
printf("%s\n",str);
}
运行情况如下:
abcdefgc ↙ (输入str)
c ↙ (输入要删去的字符)
abdefg (输出已删去指定字符的字符串)