《C语言课件第08章函数.ppt》由会员分享,可在线阅读,更多相关《C语言课件第08章函数.ppt(100页珍藏版)》请在三一办公上搜索。
1、,第八章,函数,本章要点,函数的概念函数的定义与调用函数的递归调用 变量的作用域函数的作用域,主要内容,8.1 概述 8.函数定义的一般形式 8.函数参数和函数的值 8.函数的调用 8.函数的嵌套调用 8.函数的递归调用 8.数组作为函数参数 8.8 局部变量和全局变量 8.变量的存储类别 8.10 内部函数和外部函数,8.1概述,一个较大的程序可分为若干个程序模块,每一个模块用来实现一个特定的功能。在高级语言中用子程序实现模块的功能。子程序由函数来完成。一个程序可由一个主函数和若干个其他函数构成。,由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。,函
2、数间的调用关系,#include void main()void printstar();/*对printstar函数声明*/void print_message();/*对print_message函数声明*/printstar();*调用printstar函数*print_message();/*调用print_message函数*/printstar();*调用printstar函数*/,例8.1先举一个函数调用的简单例子,void printstar()*定义printstar函数*printf(*n);void print_message()*定义print_message函数*pr
3、intf(How do you do!n);,运行情况如下:*How do you do!*,说明:(1)一个程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对较大的程序,一般不希望把所有内容全放在一个文件中,而是将他们分别放在若干个源文件中,再由若干源程序文件组成一个C程序。这样便于分别编写、分别编译,提高调试效率。一个源程序文件可以为多个C程序公用。,(2)一个源程序文件由一个或多个函数以及其他有关内容(如命令行、数据定义等)组成。一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的。,(3)程序的执行是从函数开始的,如是在
4、函数中调用其他函数,在调用后流程返回到函数,在函数中结束整个程序的运行。,(4)所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另一函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用函数。函数是系统调用的。,(5)从用户使用的角度看,函数有两种:标准函数,即库函数。这是由系统提供的,用户不必自己定义这些函数,可以直接使用它们。应该说明,不同的C系统提供的库函数的数量和功能会有一些不同,当然许多基本的函数是共同的。用户自己定义的函数。用以解决用户的专门需要。,(6)从函数的形式看,函数分两类:无参函数。如例8.1中的printstar和print_messa
5、ge就是无参函数。在调用无参函数时,主调函数不向被调用函数传递数据。无参函数一般用来执行指定的一组操作。例如,例8程序中的printstar函数。有参函数。在调用函数时,主调函数在调用被调用函数时,通过参数向被调用函数传递数据,一般情况下,执行被调用函数时会得到一个函数值,供主调函数使用。,8.函数定义的一般形式,8.2.1.无参函数的定义一般形式,定义无参函数的一般形式为:类型标识符函数名()声明部分 语句部分,在定义函数时要用“类型标识符”指定函数值的类型,即函数带回来的值的类型。例8.中的printstar和print_message函数为void类型,表示不需要带回函数值。,8.2.2
6、.有参函数定义的一般形式,定义有参函数的一般形式为:类型标识符函数名(形式参数表列)声明部分 语句部分,例如:(int,int);/*函数体中的声明部分*?;();,8.2.3 空函数,定义空函数的一般形式为:类型标识符函数名()例如:(),调用此函数时,什么工作也不做,没有任何实际作用。在主调函数中写上“();”表明“这里要调用一个函数”,而现在这个函数没有起作用,等以后扩充函数功能时补充上。,8.函数参数和函数的值,8.形式参数和实际参数,大多数情况下,主调函数和被调用函数之间有数据传递的关系。,在不同的函数之间传递数据,可以使用的方 法:参数:通过形式参数和实际参数返回值:用return
7、语句返回计算结果全局变量:外部变量,#include void()int max(int,int);/*对函数的声明*/int,;scanf(,);(,);printf(,);,例8.调用函数时的数据传递,int max(int,int)*定义有参函数max*int;?;return();,运行情况如下:,,通过函数调用,使两个函数中的数据发生联系,关于形参与实参的说明:,(1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。,(2)实参可以是常量、变量或表达式,如:(,);但
8、要求它们有确定的值。在调用时将实参的值赋给形参。,(3)在被定义的函数中,必须指定形参的类型(见例8.2程序中的“(,);”)。,(4)实参与形参的类型应相同或赋值兼容。例8中实参和形参都是整型。如果实参为整型而形参x为实型,或者相反,则按第3章介绍的不同类型数值的赋值规则进行转换。,例如实参值a为3.5,而形参x为整型,则将实数3.5转换成整数3,然后送到形参b。字符型与整型可以互相通用。,(5)在语言中,实参向对形参的数据传递是“值传递”,单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。,在调用函数时,给形参分配存储单元,并将实参对应的值传递
9、给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。因此,在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数的实参的值。例如,若在执行函数过程中和的值变为和,而和仍为和。,8.3.2 函数的返回值,通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。例如,例8.中,(,)的值是,(,)的值是5。赋值语句将这个函数值赋给变量。,关于函数返回值的一些说明:,(1)函数的返回值是通过函数中的return语句获得的。,如果需要从被调用函数带回一个函数值供主调函数使用,被调用函数中必须包含return语句。如果不需要从被调用函数带回函数值可以不要return语
10、句。,一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个语句起作用。return语句后面的括弧也可以不要,如:“return;”等价于“return();”,return语句将被调用函数中的一个确定值带回主调函数中去。见图8.2中从return语句返回的箭头。,return后面的值可以是一个表达式。例如,例8中的函数可以改写成:,(int,int)(?);,(2)函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。,例如:下面是3个函数的首行:int max(float,float)/*函数值为整型*/char letter(char c1,c
11、har c2)/*函数值为字符型*/double min(int,int)/*函数值为双精度型*/,在语言中,凡不加类型说明的函数,自动按整型处理。例8.中的函数首行的函数类型int可以省写,用Turbo C 2.0编译程序时能通过,但用Turbo C 3.0编译程序时不能通过,因为C+要求所有函数都必须指定函数类型。因此,建议在定义时对所有函数都指定函数类型。,(3)在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。,如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。,(4)对于不带回
12、值的函数,应当用“void”定义函数为“无类型”(或称“空类型”)。这样,系统就保证不使函数带回任何值,即禁止在调用函数中使用被调用函数的返回值。此时在函数体中不得出现return语句。,例 8.返回值类型与函数类型不同,#include void main()int(float,float);float,;int;scanf(,);(,);printf(,);int max(float,float)float;/*z为实型变量*/?;return();,运行情况如下:,Max is,8.函数的调用,8.函数调用的一般形式,函数调用的一般形式为:函数名(实参表列),如果实参表列包含多个实参,则
13、各参数间用逗号隔开。实参与形参的个数应相等,类型应匹配。实参与形参按顺序对应,一一传递数据。,如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略。,例 8 实参求值的顺序,#include void main()int f(int a,int b);/*函数声明*/int i=2,p;p=f(i,+i);/*函数调用*/printf(%dn,p);,int f(int a,int b)/*函数定义*/int c;if(ab)c=1;else if(a=b)c=0;else c=-1;return(c);,如果按自左至右顺序求实参的值,则函数调用相当于(,),如果按自左至右顺序求实参的值
14、,则函数调用相当于(3,),对于函数调用 int i=2,p;p=f(i,+i);,8.函数调用的方式,按函数在程序中出现的位置来分,可以有以下三种函数调用方式:,函数语句把函数调用作为一个语句。如例8.1中的printstar(),这时不要求函数带回值,只要求函数完成一定的操作。,函数表达式函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参加表达式的运算。例如:*(,);,函数参数函数调用作为一个函数的实参。例如:m=max(a,max(b,c);其中max(b,c)是一次函数调用,它的值作为max另一次调用的实参。m的值是a、b、c三者中的最大者。又如:pr
15、intf(%d,max(a,b);也是把max(a,b)作为printf函数的一个参数。函数调用作为函数的参数,实质上也是函数表达式形式调用的一种,因为函数的参数本来就要求是表达式形式。,8.对被调用函数的声明和函数原型,在一个函数中调用另一函数(即被调用函数)需要具备哪些条件呢?,(1)首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一条件还不够。,8.对被调用函数的声明和函数原型,(3)如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调用的函数作声明。,(2)如果使用库函数,还应该在本文件开头
16、用命令将调用有关库函数时所需用到的信息“包含”到本文件中来。,函数原型的一般形式为(1)函数类型 函数名(参数类型1,参数类型2);(2)函数类型 函数名(参数类型1,参数名1,参数类型2,参数名2);,“声明”一词的原文是declaration,过去在许多书中把它译为“说明”。声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。(例如函数名是否正确,实参与形参的类型和个数是否一致)。,注意:函数的“定义”和“声明”不是一回事。函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它是一
17、个完整的、独立的函数单位。而函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。,#include void main()float add(float x,float y);*对被调用函数add的声明*float a,b,c;scanf(f,f,a,b);cadd(a,b);printf(sum is f n,c);float add(float,float)*函数首部*float;/*函数体*/z;return(z);,例8 对被调用的函数作声明,如果 被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已
18、经先知道了已定义函数的有关情况,会根据函数首部提供的信息对函数的调用作正确性检查。,如果 被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已经先知道了已定义函数的有关情况,会根据函数首部提供的信息对函数的调用作正确性检查。,改写例 8.,#include float add(float,float)*函数首部*float;/*函数体*/z;return(z);void main()float a,b,c;scanf(f,f,a,b);cadd(a,b);printf(sum is f n,c);,函数举例,例1 计算10!+9!+6!例2 写一个函数使输入的字符串按反次序存放
19、,然后输出。,作业,输入10个数x,计算下列函数值。编写程序计算1+(1+2)+(1+2+3)+(1+n)编写函数计算m!/(m-n)!,函数回顾,函数定义的一般格式函数类型 函数名(形参表)/函数首部/函数体 函数实现过程 return 表达式;,函数的参数,形式参数表类型1 参数1,类型2 参数2,类型n 参数n参数之间用逗号分隔,每个参数前面的类型都必须分别写明函数定义时的参数被称为形式参数(简称形参)int max(int,int)函数调用时的参数被称为实际参数(简称实参)C=max(a,b),函数原型声明,函数类型 函数名(参数表);int max(int,int);函数声明:说明函
20、数的类型和参数的情况,以保证程序编译时能判断对该函数的调用是否正确。函数必须先定义后调用,将主调函数放在被调函数的后面,就像变量先定义后使用一样。如果自定义函数在主调函数的后面,就需要在函数调用前,加上函数原型声明。,只写函数定义中的第1行(函数首部),并以分号结束。,参数传递,实参形参在参数传递过程中,实参把值复制给形参。形参和实参一一对应:数量一致,类型一致,顺序一致形参:变量,用于接受实参传递过来的值实参:常量、变量或表达式,例 求100以内的全部素数,每行输出10个,#include#include int main(void)int count,m;int prime(int m);
21、count=0;for(m=2;m=100;m+)if(prime(m)!=0)printf(%6d,m);count+;if(count%10=0)printf(n);printf(n);,int prime(int m)int i,n;if(m=1)return 0;n=sqrt(m);for(i=2;i=n;i+)if(m%i=0)return 0;return 1;,8.函数的嵌套调用,嵌套定义就在定义一个函数时,其函数体内又包含另一个函数的完整定义。,语言不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。,8.函数的嵌套调用,例1 编写一个函数
22、计算分析 编写一个函数计算i!,然后编写函数C(n,m)计算,8.函数的嵌套调用,计算阶乘的函数 int fac(int i)int k,f=1;for(k=1;k=i;k+)f*=k;return(f);,计算C(n,m)的函数 int cnm(int n,int m)int c;c=fac(n)/(fac(m)*fac(n-m);return(c);,8.函数的嵌套调用,调用函数C(n,m)计算 void main()int c,n,m;n=9;m=5;(或 scanf(“%d%d”,函数嵌套调用的执行过程,8函数的递归调用,在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数
23、的递归调用。语言的特点之一就在于允许函数的递归调用。,例 8 有个人坐在一起,问第个人多少岁?他说比第个人大岁。问第个人岁数,他说比第个人大岁。问第个人,又说比第个人大岁。问第个人,说比第个人大岁。最后问第个人,他说是岁。请问第个人多大。,()()()()()()()()()可以用数学公式表述如下:age(n)()age(n)=age(n-1)(),可以用一个函数来描述上述递归过程:int age(int)*求年龄的递归函数*int;*用作存放函数的返回值的变量*if();else();return();用一个主函数调用age函数,求得第5人的年龄。#include void main()pr
24、intf(%d,age(5);,运行结果如下:,例8.用递归方法求!,求!也可以用递归方法,即!等于!,而!,!。可用下面的递归公式表示:!(,)()!(),long fac(int n)long f;if(n=0)f=1;else f=n*fac(n-1);return f;,例8.8 用递归函数计算N!,例8.9用递归法计算n阶勒让德多项式的值,Float p(int n,float x)float q;if(n=0)q=1.0;else if(n=1)q=x;else q=(2*n-1)*x*p(n-1,x)-(n-1)*p(n-2,x)/n;return(q);,8.数组作为函数参数,
25、数组作函数的参数有以下情况:数组元素作函数的参数数组名作函数的参数8.7.1 数组元素作函数的实参数组元素作函数的参数时,和普通变量做参数相同。数组元素只能作函数的实参,采用“值传送”方式。,8.7.2 数组名作函数参数,可以用数组名作函数参数,此时形参应当用数组名或用指针变量。形参数组可不指定大小。,例 8.13 用选择法对数组中10个整数按由小到大排序。,程序实例#include void main()void sort(int array,int);int a10,i;printf(enter the arrayn);for(i=0;i10;i+)scanf(”%d”,i+)printf
26、(”%d”,ai);printf(n);,void sort(int array,int n)int i,j,k,t;for(i=0;in-1;i+)ki;for(j=i+1;jn;j+)if(arrayj arrayk)k=j;t=arrayk;arrayk=arrayi;arrayi=t;,8.7.3.多维数组名作函数参数,程序如下:#include void main()int max_value(int array4);int a34=1,3,5,7,2,4,6,8,15,17,34,12;printf(max value is,max_value(a));,int max_value
27、(int array 4)int i,j,k,max;max=array00;for(i=0;imax)max=array ij;return(max);,运行结果如下:Max value is 34,8.8局部变量和全局变量,局部变量在函数内定义的变量(包括形参)作用范围:本函数内部定义在复合语句内的变量作用范围:复合语句内部全局变量在函数以外定义的变量,不从属于任一函数。作用范围:从定义处到源文件结束(包括各函数),例 局部变量使用举例。,#include int main(void)int a;a=1;/*复合语句开始*/int b=2;b=a+b;a=a+b;/*复合语句结束*/pri
28、ntf(%d,a);return 0;,b:小范围内的临时变量,4,全局变量,在函数内定义的变量是局部变量,而在函数之外定义的变量称为外部变量,外部变量是全局变量(也称全程变量)。全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。,例 8.15 有一个一维数组,内放个学生成绩,写一个函数,求出平均分、最高分和最低分。,#include float Max,Min;*全局变量*void main()float average(float array,int n);float ave,score10;int;for(i=0;i10;i+)scanf(%f,&s
29、corei);ave=average(score,10);printf(“max=%6.2fnmin=%6.2fn average=%6.2fn“,Max,Min,ave);,float average(float array,int n)*定义函数,形参为数组*/int;float aver,sum=array0;Max=Min=array0;for(i=1;in;i+)if(arrayiMax)Maxarrayi;else if(arrayiMin)Min arrayi;sum=sum+arrayi;aversum/n;return aver;,建议不在必要时不要使用全局变量,原因如下:,
30、全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量的值。在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。,它使函数的通用性降低了,因为函数在执行时要依赖于其所在的外部变量。,8.变量的存储类别,8.动态存储方式与静态存储方式,前面已介绍了从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。那么从变量值存在的时间(即生存期)角度来分,又可以分为静态存储方式和动态存储方式。,所谓静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。而动态存储
31、方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。这个存储空间可以分为三部分:程序区静态存储区动态存储区,存储类别指的是数据在内存中存储的方式。存储方式分为两大类:静态存储类和动态存储类。具体包含四种:自动的()静态的()寄存器的()外部的()根据变量的存储类别,可以知道变量的作用域和生存期。,8.auto变量,函数中的局部变量,数据存储在动态存储区中 函数中的形参、在函数中定义的变量和复合语句中定 义的变量,都属auto类。在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。自动变量用关键字作存储类别的声明。例如:int(i
32、nt)*定义f函数,为形参*auto int,;*定义、为自动变量*,int(int)int,;,8.9.3用static声明局部变量,有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。这时就应该指定该局部变量为“静态局部变量”,用关键字进行声明。通过下面简单的例子可以了解它的特点。,#include void fun(int k);int main(void)int k;for(k=1;k=3;k+)fun(k);return 0;void fun(int k)static int a;p
33、rintf(%d,a);a+=k;,0,1,3,静态变量会记住前一次调用时留下来的值,静态变量的初值为0,对静态局部变量的说明:(1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释放。(2)对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。,(3)如在
34、定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的值是不确定的。(4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。,8.9.4 register变量,寄存器变量,用关键字register作声明。例如,例819中的程序是输出到n的阶乘的值。,例819 使用寄存器变量#include void main()long fac(long);long i,n;scanf(%ld,
35、8.5 用extern声明外部变量,外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。有时需要用extern来声明外部变量,以扩展外部变量的作用域。,1.在一个文件内声明外部变量,例820 用extern声明外部变量,扩展它在程序文件中的作用域。#include void main()int max(int,int);*外部变量声明*extern A,B;printf(%dn,max(A,B);int A=13,B=-8;*定义外部变量*int max(int x,int
36、 y)*定义函数*int z;z=xy?x:y;return(z);,2.在多文件的程序中声明外部变量,例8 用extern将外部变量的作用域扩展到其他文件。本程序的作用是给定的值,输入和,求和am的值。文件file中的内容为:#include int A;/*定义外部变量*/void main()int(int);/*函数声明*/int,;printf(enter the number a and its power m:n);scanf(,A,);A*;printf(*,A,);();printf(*n,A,);,文件file中的内容为:extern A;/*声明A为一个已定义的外部变量*
37、/int(int);int,;for(;)*A;();,8.9.6用static声明外部变量,有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加一个声明。例如:file1.c file2.cstatic int A;extern int A;/*cant use A in file1.cvoid main()void fun(int n)A=A*n;,例1 main()int i;for(i=0;i3;i+)add();add()static int x=0;x+;printf(“%dn”,x);,例2 main()int x=10;func(
38、x);printf(“%d”,x);func(int x)x=20;,例3int x=1;main()func(x);printf(“%d”,x);func(int x)x=3;,例4 main()int x=10;int x=20;printf(“%d”,x);printf(“%dn”,x);,例5int n=7;main()extern int n;printf(“n=%d”,(f(+n),n);void f(int t)n=(t+1)*(n+1);,8.8存储类别小结,(1)从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:局部变量|自动变量,即动态局部变量(离开函数,值就消
39、失)|静态局部变量(离开函数,值仍保留)|寄存器变量(离开函数,值就消失)|(形式参数可以定义为自动变量或寄存 器变量)全局变量|静态外部变量(只限本文件引用)|外部变量(即非静态的外部变量,允许其他文件引用),从变量存在的时间(生存期)来区分,有动态存储和静态存储两种类型。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。动态存储|自动变量(本函数内有效)|寄存器变量(本函数内有效)|形式参数(本函数内有效)静态存储|静态局部变量(函数内有效)|静态外部变量(本文件内有效)|外部变量(其他文件可引用),(3)从变量值存放的位置来区分,可分为:内存中静态存储区|静态局部
40、变量|静态外部变量(函数外部静态变量)|外部变量(可为其他文件引用)内存中动态存储区:自动变量和形式参数CPU中的寄存器:寄存器变量,()关于作用域和生存期的概念。从前面叙述可以知道,对一个变量的性质可以从两个方面分析,一是变量的作用域,一是变量值存在时间的长短,即生存期。前者是从空间的角度,后者是从时间的角度。二者有联系但不是同一回事。,(5)static对局部变量和全局变量的作用不同。对局部变量来说,它使变量由动态存储方式改变为静态存储方式。而对全局变量来说,它使变量局部化(局部于本文件),但仍为静态存储方式。从作用域角度看,凡有static声明的,其作用域都是局限的,或者是局限于本函数内
41、(静态局部变量),或者局限于本文件内(静态外部变量)。,8.10 内部函数和外部函数,函数本质上是全局的,因为一个函数要被另外的函数调用,但是,也可以指定函数不能被其他文件调用。根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数。,8.10.1内部函数,如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。即static 类型标识符 函数名(形参表)如 static int fun(int a,int b),8.10.2外部函数,(1)在定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其
42、他文件调用。如函数首部可以写为extern int fun(int a,int b)这样,函数fun就可以为其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。本书前面所用的函数都是外部函数。,(2)在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数,例 8.22 有一个字符串,内有若干个字符,今输入一个字符,要求程序将字符串中该字符删去。用外部函数实现,File.c(文件)#include void main()extern void enter_string(char str);extern void detele_str
43、ing(char str,char ch);extern void print_string(char str);*以上3行声明在本函数中将要调用的在其他文件中定义的3个函数*char c;char str80;scanf(%c,file(文件)#include void enter_string(char str80)*定义外部函数 enter-string*gets(str);*向字符数组输入字符串*file(文件)void delete_string(char str,char ch)*定义外部函数 delete_string*int i,j;for(i=j=0;stri!=0;i+)if(stri!=ch)strj+=stri;stri=0;,file(文件)#include void print_string(char str)printf(%sn,str);,运行情况如下:(输入)(输入要删去的字符)(输出已删去指定字符的字符串),