1
第八章 函 数主讲 福州大学数学与计算机学院 韩晓芸
E-mail,hxy@fjtv.net
第八章 函 数
2
第八章 函 数第一节 C函数概述第二节 C函数的定义第三节 函数调用和参数传递第四节 函数的嵌套调用第五节 函数的递归调用第六节 数组作为函数的参数第七节 局部变量和全局变量第八节 变量的存储类别第九节 内部函数和外部函数第十节 如何运行一个多文件程序第八章 函 数
3
第一节 C函数概述一个C程序是由 头部说明 和 若干函数 构成的。
其中必须包含且仅包含 1个主函数,它是整个程序运行的起点。
非主函数 常用来实现某个特定的功能模块。
主函数 可以调用其他函数,其他函数也可以互相调用。
在程序设计中,常 将一些常用的功能模块编写成函数,放在函数库中供公共选用 。利用库函数,可以减少重复编写程序段的工作量。
函数必须 先定义,后调用 。
第八章 函 数
4
源文件 1
函数 n
函数 2
函数 1
源文件 m
源文件 2
一个C
程序
一个C
程序的源文件
......,.....
第一节 C函数概述第八章 函 数
5
第一节 C函数概述
print_mess()
{printf
(" How do you do!\n");
}
printstar()
{printf
("*********\n");
}
例 1
main()
{
printstar();
print_mess();
printstar();
}
第八章 函 数
6
第一节 C函数概述函数分类:
标准函数 (库函数),指由系统定义的函数,
用户程序可以调用,要加头文件说明 。
自定义函数,指由用户根据实际需要编写的函数,
可以和主函数写在同一个文件中,直接进行调用,
无需头文件说明 ; 也可以单独写成,h头文件中,
调用同标准函数。
.h文件可以书写若干个函数的定义第八章 函 数
7
第二节 C函数的定义
无参函数的定义
– 类型标识符 函数名()
– {声明部分 /*定义局部变量 */
– 语句 } /*函数体 */
上例中 print_star()和 print_mess()都是无参函数。
说明:
1.,类型标识符”指定函数返回值的类型,即函数带回来的值的类型。无参函数一般不需要带回函数值,因此可以不写类型标识符,或者用 void标识。
2,虽然无参数,函数的括号要保留 。
第八章 函 数
8
第二节 C函数的定义
有参函数的定义
– 类型标识符 函数名( 形式参数表列 )
– {声明部分
– 语句 }
说明:
1.有参数函数可能有返回值,也可能没有返回值,有返回值时要标明正确的返回值类型。且在函数体中至少有一个 return语句 。
2.参数表中的多个参数用 逗号 隔开,且 每个参数的类型要定义。
第八章 函 数
9
第二节 C函数的定义有参函数的定义形式:
例,int max( int x,int y)
{ int z ; /*声明部分 */
z= x>y?x:y;
return(z);}
注,形参的类型 可以在括号中定义,也可以在 ()

{前来定义。即上述定义形式可改写为如下:
int max(x,y)
int x,y;
{int z; z=x>y?x:y;
return z; }
第八章 函 数
10
第三节 函数调用和参数传递
3.1 函数调用的一般形式
3.2 函数的调用方式
3.3 函数返回值
3.4 形参和实参的几点说明
3.5 被调用 函数的说明第八章 函 数
11
形式参数,在 定义函数时,函数名后面的括号中的 变量 称为形式参数,简称 形参 。 即在函数被调用之前形参没有具体的实际值。
实际参数,在 调用函数时,函数名后面括号中的表达式称为实际参数,简称 实参 。 实参可以是 常量、变量、表达式,但必须有具体可计算的值。
3.1 函数调用的一般形式第八章 函 数
12
例 2:
int max(int x,int y)
{int z;
if(x>=y) z=x;
else z=y;
return(z);}
main( )
{int a,b,c;
scanf("%d%d",&a,&b);
c=max(a,b);
printf("max is %d\n",c);}
形式参数 x,y
实际参数 a,b
第八章 函 数
13
3.1 函数调用的一般形式函数名 (实参表) ;
例如,exch(a,b);
int exch(int x,int y)
实参 自右向左 的 逐个 传递给形参 。
第八章 函 数
14
例 3:
main()
{ int i;
i=1;test(i++,i);
i=1;test(i,i++);
}
test(int x,int y)
{ printf("x=%d\ty=%d\n",x,y); }
执行后,
x=1 y=1
x=2 y=1
3.1 函数调用的一般形式第八章 函 数
15
3.2 函数的几种调用方式
printstar();
print_mess();
c=max(a,b);
1.函数调用作为语句
2.表达式中的函数调用
3.函数调用作为一个函数的实际参数
printf("%d\n",max(a,b));

m= max(a,max(b,c));
第八章 函 数
16
3.3 函数返回值
一个函数的返回值是通过 return语句获得的
return后面 可以是一个表达式
如果 return语句中表达式的类型 与 函数值的类型不一致,以函数类型为准 。 (见例 4、例 5)
如果 被调函数中 没有 return语句,则带回的是一个不确定的值 。 为了明确表示不带返回值,
可以 用 void定义无类型第八章 函 数
17
例 4:
int max(float x,float y)
{float z;
if(x>=y) z=x;
else z=y;
return(z);}
3.3 函数返回值
main()
{float a,b;int c;
scanf("%f%f",&a,&b);
c=max(a,b);
printf("%d\n",c);}
函数返回值的类型是 int
return语句中表达式的类型是 float
第八章 函 数
20
1,在调用函数时,主调函数和被调用函数之间有数据传递关系。即把实参的值按照 位置 一一传递给形参,使形参有了具体值。但形参改变后的值不能回传给实参,即值是 单向传递 的。
2 5
2 5
a b
x y
2 5
5 2
a b
x y
3.4 形参和实参的几点说明第八章 函 数
21
例 6:
main()
{int a,b;scanf("%d,%d",&a,&b); /*键入 2,5*/
printf(“a=%d,b=%d\n”,a,b); /*显示 a=2,b=5*/
exch(a,b);
printf(“a=%d,b=%d\n”,a,b);} /*显示 a=2,
b=5*/
int exch(int x,int y)
{int t;
printf(“x=%d,y=%d\n”,x,y); /*显示 x=2,
y=5*/
t=x; x=y; y=t;
printf(“x=%d,y=%d\n”,x,y);} /*显示 x=5,
实际参数 a,b
形式参数 x,y
第八章 函 数
22
2,形参的有效范围是,在其定义所在的函数内。
形参 仅当其定义所在的函数被执行时,系统才为其分配存储空间,在未出现函数调用时,它们并不占内存的存储单元,并且 在函数调用结束后,
该存储空间即被系统收回。 思考,例 6在主函数的最后插入:
printf("x和 y是,%d,%d\n",x,y);
结果?!
3.4 形参和实参的几点说明第八章 函 数
23
3,对于有参函数,调用语句中的实参个数要等于被调用函数的形参个数。
4、实参传递给形参的实际值必须与函数定义中的参数 类型一致 。 不一致时要 在主调函数中对被调函数作 原型声明 。
3.4 形参和实参的几点说明第八章 函 数
24
3.5 被调用函数的声明和函数原型
1.被调函数必须是 已经存在 的函数。
2.若使用库函数,必须在本文件开头加上
#include命令 将调用有关库函数时所用的信息包含到本文件中。
例如,#include,stdio.h”
3.若调用自己定义的函数,函数调用语句之前使用函数说明语句对该被调用函数作原型声明 。
第八章 函 数
25
3.5 被调用函数的声明和函数原型
3、函数声明为了保证函数定义和函数调用的一致性,C语言要求函数在调用前应该进行声明,以便程序编译时进行对照检查。
形式,函数类型 函数名 (形参表 );
如,int max(int x,int y);
定义 函数时 )后是没有 ;号的。而 声明 函数是用上面的语句来实现。
第八章 函 数
26
3.5 被调用函数的声明和函数原型
3、函数声明形参表中也可以仅书写形参类型,不书写形参名称 。 如,long fun(int,int);
函数声明也称为,函数原型,,原型只是在函数被调用之前对 函数类型,函数名,形参类型,形参个数 进行说明,不同于函数的定义。
第八章 函 数
27
3.5 被调用函数的声明和函数原型
3、函数声明函数声明的位置:
( 1) 放在主调函数内声明,称,内部声明,。
( 2) 放在所有函数前面声明,即程序头部的最后部分,也就是 include和 define之后,称,外部声明,。
第八章 函 数
28
3.5 被调用函数的声明和函数原型
3、函数声明以下情况可以不声明:
( 1)被调用函数的定义在主调用函数之前。
( 2)被调用函数的定义单独写在一个,h文件中,
以 #include语句说明。(同 标准函数 的头文件声明)
第八章 函 数
29
1.被调用函数的定义在主调函数之后,必须做函数原型声明。 例 7:
main()
{double power(int,float); int i;
for(i=1;i<9;i++)
printf(“%d %f\n”,i,power(i,1.5));}
double power(int n,float x) /*函数定义 */
{int i; double p=1;
for(i=1;i<=n;i++) p=p*x;
return(p);}
第八章 函 数
30
2,被调用函数的定义在主调函数之前,可以免去函数的原型声明。 例 8:
double power(int n,float x)
{int i; double p=1;
for(i=1;i<=n;i++) p=p*x;
return(p);}
main()
{int i;
for(i=1;i<9;i++)
printf(“%d %f \n”,i,power(i,1.5));}
第八章 函 数
31
3.如果在所有函数之前,即在文件的开头已说明了函数类型,则在各个主调函数中不必对所调用的函数再做类型说明。如下例:
float f(float,float);
char ch(char,char);
main()
{..,}
float f(float x,float y)
{… }
char ch(char c1,char c2)
{… }
第八章 函 数
32
第四节 函数的嵌套调用
C语句允许嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。 每一个函数调用结束,即执行了 return语句,则 返回上一级主调函数的函数调用语句后的语句继续执行 。 具体运行的情形见下图所示。
第八章 函 数
33
主函数 main的作用开始执行程序总控作用结束程序主函数 main
第八章 函 数
34
例 9:
#include <stdio.h>
b()
{putchar('b');}
a()
{putchar('a'); b(); putchar('a');}
c()
{putchar('c'); a(); putchar('c'); }
main()
{c(); }
该程序执行后显示 cabac
第八章 函 数
35
例 10,计算 s=(1!)+(1!+2!)+…+(1!+…+n!) 。 n由用户输入,小于 10。
分析:由题意可知,整个计算是 累加 运算。要累加的每个数据项又是一个 累加 运算,而数据项的累加运算中又是一个 连乘 运算 。
设计:函数 h1()计算阶乘;函数 h2()计算每个数据项的累加运算。
第八章 函 数
36
例 10 的源程序代码编写如下:
long h1(int n)
{int i;long p=1;
for(i=1;i<=n;i++)
p=p*i;
return(p);}
long h2(int m)
{int k;
long s=0;
for(k=1;k<=m;k++)
s=s+h1(k);
return(s);}
main()
{int x,j;
long t=0;
printf("input a int(<10):\n");
scanf("%d",&x);
for(j=1;j<=x;j++)
t=t+h2(j);
printf("1!+(1!+2!)+...+(1!+2!+.
..+ %d!)= %ld\n",x,t);
}
第八章 函 数
37
在一个函数的定义中出现 直接 /间接 调用该函数本身的现象称为函数的递归调用 。
递归调用的特点,
1) 把一个问题转化为一个新问题,新问题与原问题解法相同,只是所处理的对象有所不同,
并且它们只是有规律的递增或递减 。
2) 必须有某个 终止递归 的条件。
第五节 函数的递归调用第八章 函 数
38
main()
{int n; long y;
scanf(“%d”,&n);
y=fac(n);
printf(“%d!=%ld\n”,n,y); }
例 11,用递归方法求 n!。
1 n=0,1
n! =
n*(n-1)! n>1
6!--5!--4!--3!--2!--1!=1(已知条件 )
1!*2--2!*3--3!*4--4!*5--5!*6=720
long fac(int m)
{long f;
if(m==0||m==1) f=1;
else f=fac(m-1)*m;
return(f);}
第八章 函 数
39
例 12,有五个人做在一起,问第五个人多少岁?他说比第四个人大 2岁;问第四个人多少岁?他说比第三个人大 2岁;问第三个人多少岁?他说比第二个人大 2
岁;问第二个人多少岁?他说比第一个人大 2岁。最后问第一个人,他说他 10岁,请问第五个人多大?
分析,age(5)=age(4)+2 age(4)=age(3)+2
age(3)=age(2)+2 age(2)=age(1)+2
age(1)=10
可以用式子表示如下:
10 (n=1)
age(n)=
age(n-1)+2 (n>1)
第八章 函 数
40
age(int n)
{int c;
if(n==1) c=10;
else c=age(n-1)+2;
return(c);}
main()
{int m;
printf("input a data:\n");
scanf("%d",&m);
printf("age(%d)=%d\n",m,age(m));}
例 12 源程序编写如下:
运行时若输入 5,则输出结果为,age(5)=18
第八章 函 数
41
例 13,某数列为 K(n)的定义为:求该数列的第六项 k(6)
1 n=1
k(n)= k(n-1)× 2 n为偶数
k(n-1)× 3 n为奇数
k(6)--k(5)--k(4)--k(3)--k(2)--k(1)=1(已知条件 )
k(1)*2--k(2)*3--k(3)*2--k(4)*3--k(5)*2--k(6)=72
源程序编写如下:
long k(int n)
{long m;
if(n==1) m=1;
else if(n%2==0) m=k(n-1)*2;
else m=k(n-1)*3;
return(m);}
第八章 函 数
42
例 13(续)
main( )
{int i; long j;
printf("input a data to i:\n:");
scanf("%d",&i);
if(i<=0) printf(“i<=0,data error!");
else
{j=k(i); printf("\nk(%d)=%ld",i,j);}}
第八章 函 数
43
第六节 数组作为函数的参数
数组元素 作为函数的实际参数
例 14,求正整数数组 a[10]中的所有 素数。
int sushu(int x)
{int i,k=1;
if(x==1) k=0;
for(i=2;i<=x/2;i++)
if(x%i==0) k=0;
return(k); }
第八章 函 数
44
第六节 数组作为函数的参数
main( )
{int a[10],i;
printf("input 10 data:\n ");
for(i=0;i<10;i++) scanf("%d",&a[i]);
printf("output 10 data:\n ");
for(i=0;i<10;i++) printf("%5d",a[i]);
printf("\noutput sushu:\n ");
for(i=0;i<10;i++)
if(sushu(a[i])) printf("%5d",a[i]);}
第八章 函 数
45
第六节 数组作为函数的参数
数组元素 作为函数的实际参数例 15,有两个数组 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数组,请分别统计出这两个数组相应元素大于、等于、小于的次数。
第八章 函 数
46
第六节 数组作为函数的参数
void main ( )
{int large(int x,int y); /* 函数声明 */
int a[10],b[10],i,n=0,m=0,k=0;
for(i=0;i<10;i++)
scanf(“%d”,&a[i]);/*逐个输入数组 a的元素 */
printf(“\n”);
for(i=0;i<10;i++)
scanf(“%d”,&b[i]);/*逐个输入数组 b的元素 */
printf(“\n”);
第八章 函 数
47
第六节 数组作为函数的参数例 15,(续 )
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 (“a>b -------\n”);
else if(n<k) printf (“a<b-------- \n”);
else printf (“a ==b -------- \n”);}
第八章 函 数
48
第六节 数组作为函数的参数例 15:( 续)
large(int x,int y)
{int flag;
if(x>y) flag=1 ;
else if(x<y) flag=-1 ;
else flag=0 ;
return (flag) ; }
第八章 函 数
49
数组名作为函数的实际参数
C语言中的 数组名代表了数组在内存中存放的起始地址,所以数组名作为函数的参数传递的是数组的起始地址,属于,传地址,,是一种 双向传递 的方式 。
第六节 数组作为函数的参数第八章 函 数
50
说明:
( 1)数组名作函数参数,应在主调函数和被调函数中分别定义数组 ;
( 2)实参数组与形参数组 类型应一致 ;
( 3)实参数组与形参数组大小可以一致也可以不一致 。 形参数组可以不指定大小 。另外设一个参数,传递数组元素的个数。( 见例 17)
( 4)数组名作函数参数时,是,地址传送,。
形参数组中各元素的值如发生变化会使实参数组元素的值同时发生变化。 ( 见例 18)
第八章 函 数
51
第六节 数组作为函数的参数
数组名作为函数的实际参数
例 16,有一个一维数组,内放 10个学生成绩,求平均成绩。
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); }
第八章 函 数
52
第六节 数组作为函数的参数
例 16(续 ):
void main()
{ int i; float aver,score[10] ;
for(i=0;i<10;i++)
scanf(“%f”,&score[i]);
printf(“\n”);
aver=average(score);
printf(“average score is %5.2f\n”,aver); }
第八章 函 数
53
第六节 数组作为函数的参数
形参数组也可以不指定大小 。另外设一个参数,传递数组元素的个数。 例 16可改写为 例 17,
源程序如下:
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); }
第八章 函 数
54
第六节 数组作为函数的参数源程序 (续 )如下:
void main( )
{ float score_1[5]={98.5,97,91.5,60,55};
float score_2[10]={67,89,99,69,77,88,75,60,91,53};
printf(“aver of a is %6.2f\n”,average(score_1,5));
printf(“aver of b is %6.2f\n”,average(score_2,10));
}
第八章 函 数
55
第六节 数组作为函数的参数
数组名作为函数的实际参数数组名作函数参数时,是,地址传送,。 形参数组中各元素的值如发生变化会使实参数组元素的值同时发生变化。
例 18 用 选择法 对数组中 10个整数由小到大排序所谓选择法就是先将 10个数中最小的数与 a[0]对换;
再将 a[1]到 a[9]中最小的数与 a[1]对换 …… 每比较一轮,
找出一个未经排序的数中最小的一个。共比较 9轮。
第八章 函 数
56
第六节 数组作为函数的参数例 18,源程序如下:
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]) k=j;
if(k!=i)
{t=array[k];
array[k]=array[i];
array[i]=t;} } }
第八章 函 数
57
第六节 数组作为函数的参数例 18,源程序 (续 )如下:
void main()
{int a[10],i;
printf(“input the array a:\n”);
for(i=0;i<10;i++)
scanf(“%d”,&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf(“%5d”,a[i]);
printf(“\n”); }
第八章 函 数
58
用多维数组作函数参数
( 1)用 多维数组元素 作函数实参
( 2)用 多维数组名 作函数实参和形参,在被调用函数中对形参数组定义时可以指定每一维的大小,
也可以省略第一维的大小说明 。
例 19 有一个 3× 4的矩阵,求其中的最大元素 。
要求,将计算最大值的功能设计成函数,数组名作为参数。
第八章 函 数
59
第六节 数组作为函数的参数例 19,源程序如下:
main( )
{ int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};
printf (“\nmax=%d\n",max_value(a));}
max_value(int array[12])
{ int i,max; max=array[0];
for(i=1;i<12;i++)
if(array[i]>max) max=array[i];
return(max); }
第八章 函 数
60
第七节 局部变量和全局变量按照 变量的作用范围,变量分为,
局部变量
– 在一个 函数或复合语句内部 定义的变量,也称内部变量。
– 作用范围,在定义该变量的函数或复合语句内有效。
全局变量
– 函数外 定义的变量。
– 作用范围,从定义点开始到本源文件结束 。
第八章 函 数
61
局部变量举例,
float f1(int a)
{int b,c; a,b,c有效
…}
int f2(int x,int y)
{int b,c,i,j; x,y,b,c,i,j有效
…}
main()
{int m,n; m,n有效
...}
说明,
形参 是局部变量
main函数的变量也是局部的
不同函数中变量的名字可以相同
复合语句内可以定义变量第八章 函 数
62
例 20
main()
{int a,b=1,c=2;
{int a,b=3; a=b+c;
printf("a=%d\n",a);
}
a=b+c;
printf("a=%d\n",a);
}
a=5
a=3
第八章 函 数
63
全局变量举例,
int k=1; /*全局变量 */
float f1(int a)
{int b,c; a,b,c有效
…}
char c1; /*全局变量 */
int f2(int x,int y)
{int i,j; x,y,i,j有效
…}
double p; /*全局变量 */
main()
{int m,n; m,n有效
,..}
P的作用范围
c1的作用范围
k的作用范围第八章 函 数
64
外部变量 与 局部变量 同名时,会屏蔽外部变量,局部变量优先 。 请尽量不用全局变量 。
例 21:
int a=3,b=5; /*全局变量 */
max(int a,int b)
{int c; c=a>b?a:b;
return c;}
main()
{int a=6; /*局部变量 */
printf(“%d\n”,max(a,b));}
程序运行结果为,6
第八章 函 数
65
如果在变量定义之前的函数想引用该外部变量,则应用 extern作外部变量说明。 例 22:
int max(int x,int y)
{ int z;
z=x>y?x:y;
return(z); }
main( )
{ extern a,b;
printf ("%d\n",max(a,b));}
int a=13,b=-8;
外部变量说明外部变量定义第八章 函 数
66
例 23,在一维数组中存放 10个学生的成绩,写一个函数,求出平均分,最高分和最低分。
分析,1,平均分由函数值带回
2,设 max,min为全局变量
score 10 max min
array n max min
main( )
average( )
第八章 函 数
67
例 23:源程序代码如下:
float max,min;
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); }
第八章 函 数
68
例 23:源程序代码(续)如下:
main( )
{ float ave,score[10];
int i;
for (i=0;i<10;i++)
scanf ("%f",&score[i]);
ave=average(score,10);
printf ("%6.2f,%6.2f,%6.2f\n",max,min,ave);
}
第八章 函 数
69
第八节 变量的存储类别变量的存储器类别寄存器静态存储区内存动态存储区
CPU 寄存器变量 (register)
静态存储区变量 (stasic)
动态存储区变量 (auto)
第八章 函 数
70
第八节 变量的存储类别
1、动态存储变量与静态存储变量
(1) 静态存储变量,程序运行期间 分配固定的 存储空间,存放全局变量或静态局部变量 。
(2)动态存储变量,根据需要 动态分配 存储空间,
存放,
1) 函数的形参变量 ;
2) 动态局部变量(未加 static 说明的);
3) 函数调用时的现场保护和返回地址等 。
第八章 函 数
71
第八节 变量的存储类别
2、局部变量的存储方式
(1)动态局部变量,( [ auto ] )
1)函数调用后,值不予保留,即释放存储空间
2)再次调用时,原值不能引用 。
3)没有说明默认定义的变量均为局部动态变量
(2)静态局部变量,( static )
1) 函数调用后保留原值,不释放所占存储空间
2) 再次调用时,原值在本函数内仍可使用 。
第八章 函 数
72
第八节 变量的存储类别例 24:
int f(int a)
{ int b=0;
static int c=3;
b=b+1;c=c+1
return(a+b+c); }
main( )
{ int a=2,i;
for (i=0;i<3;i++)
printf("%5d",f(a));
printf(“\n”);}
结果呢?
第几次调用调用时初值 调用结束时的值
b bc c a+b+c
1
2
3
0
0
0
3
4
5
1
1
1
4
5
6
7
8
9
第八章 函 数
73
long fac(int n)
{static long f=1;
f=f*n;
return f;}
main()
{int i;
for(i=1;i<=5;i++)
printf(“%d!=%ld\n”,i,fac(i));}
long fac(int n)
{int i; long f=1;
for(i=1;i<=n;i++)
f=f*i;
return f;}
main()
{int i;
for(i=1;i<=5;i++)
printf(“%d!=%ld”,i,fac(i));}
什么时候使用局部静态变量?
例 25,求 1到 5的阶乘。
第八章 函 数
74
3、全局变量的存储方式
( 1)动态全局变量
1) 在函数外部定义
2) 允许其他文件中的函数引用,引用时需要用 extern
作说明。 见例 26:
file1.c
int a; /*auto int a;*/
main()
{int m,k;
scanf("%d,%d",&a,&m);
k=power(m);
printf("%d",k);}
file2.c
extern int a;
power(n)
int n;
{int i,y=1;
for(i=1;i<=n;i++)
y=y*a;
return(y);}
第八章 函 数
75
3、全局变量的存储方式
( 2)静态全局变量
1) 在函数外部用 static定义。
2) 只能被本文件中的函数引用。
file1.c
static int a;
main()
{int m,k;
scanf("%d,%d",&a,&m);
k=power(m);
printf("%d",k);}
file2.c
extern int a;
power(n)
int n;
{int i,y=1;
for(i=1;i<=n;i++)
y=y*a;
return(y);}
这样引用是错误的第八章 函 数
76
举例:
/*TEST1.C*/
static int a; /*a只允许文件 TEST1.C中函数访问 */
f1()
{a=2; printf("函数 f1 中的 a是 %d\n",a); }
/*TEST2.C*/
int a; /*a还允许其它文件中函数访问的全局变量 */
f2()
{a=4; printf("函数 f2 中的 a是 %d\n",a); }
/*TEST3,C*/
extern f1(),f2(); /*函数 f1和 f2定义在其它文件中 */
extern int a; /*a是定义在其它文件中的全局变量,是 TEST2.C中的变量 a*/
第八章 函 数
77
举例(续):
main()
{f1();
f2();
printf("函数 main 中的 a是 %d\n",a);}
该程序执行时显示,函数 f1 中的 a是 2
函数 f1 中的 a是 4
函数 main 中的 a是 4
第八章 函 数
78
小结-按照作用范围
1)局部变量:
动态局部变量(离开 {},值就消失)
静态局部变量(离开 {},值仍保留)
寄存器变量 (离开参数,值就消失)
2)全局变量:
静态外部 (全局 )变量(只限本文件引用)
非静态外部 (全局 )变量(允许其它文件引用)
第八章 函 数
79
第九节 内部函数和外部函数根据函数能否被其它源文件调用,将函数分为 内部函数 和 外部函数 。
1、内部函数,如果一个函数只能被本文件其它函数所调用,它称为内部函数。
定义为:
static 类型标识符 函数名 (形参表 )
默认定义的函数是可以被其他文件使用的 。
第八章 函 数
80
第九节 内部函数和外部函数
2、外部函数,
能够被其他文件调用的函数称为外部函数定义如下:
[ extern ] 类型标识符 函数名 (形参表 )
在调用函数中,要进行声明,形式如下:
[ extern ] 类型标识符 函数名 (形参表 );
第八章 函 数
81
第九节 内部函数和外部函数例 27,有一个字符串,内有若干个字符,现输入一个字符,编程将字符串中该字符删去。 用外部函数实现 。
main()
{ extern
enter_string(),delete_string(),print_string();
char c;
static char str[80];
enter_string(str);
scanf("%c",&c);
delete_string(str,c);
print_string(str); }
file1.c(文件 1)
说明本文件用到其它文件中的函数第八章 函 数
82
例 27(续):
#include "string.h"
enter_string(str)
char str[80];
{ gets(str); }
delete_string(str,ch)
char str[ ],ch;
{int i,j;
for(i=j=0;str[i]!='\0';i++)
if(str[i]!=ch)
str[j++]=str[i];
str[j]='\0'; }
print_string(str)
char str[ ];
{printf("%s",str);}
file2.c(文件 2)
file3.c(文件 3)
file4.c(文件 4)
第八章 函 数
83
一,函数变量的归纳
1.变量属性定义:数据类型和存储类型。 如下表:
变量属性表存储类型 数据类型 变量名称 含 义
static int a a为静态内部变量或静态外部变量
auto char b b为自动变量,在函数内定义
register float c c为寄存器变量,在函数内定义
extern double d d是一个已被定义的外部变量小结:
第八章 函 数
84
2.变量的作用域、作用时间和在内存的存储区。
变量类别 作用域 作用时间 内存存区自动变量 局部变量 动态存储 动态存储区形式参数 局部变量 动态存储 动态存储区寄存器变量 局部变量 动态存储 CPU寄存器静态局部变量 局部变量 静态存储 静态存储区静态外部变量 全局变量 静态存储 静态存储区外部变量 全局变量 静态存储 静态存储区第八章 函 数
85
二、常见的错误
(1) 调用 C函数库函数,未加库说明。
(2) 调用函数前,未对其进行定义,也未加说明。
(3) 函数的形参与实参类型不匹配。
(4) 误认为 C的函数是“双向传递”的,遗失函数执行结果。
(5) 函数参数的求值顺序错误。
如,i=3;printf(“%d %d %d\n”,i,++i,++i);
函数的参数是从右至左运算的,因此应得正确结果:
5 5 4,当得到 3 4 5时是错误的,参数的求值顺序错了
(6) 函数值类型与主调函数中对其说明类型不一致。
第八章 函 数
86
模块化程序设计举例例 28,写出判断素数的函数,在主函数中输入一个整数,输出是否是素数的信息。
main()
{int m,n; scanf(“%d”,&m); n=prime(m);
if(n) printf(“%d is a prime number\n”,m);
else printf(“%d is not a prime number\n”,m);}
int prime(int m)
{int i;
for(i=1;i<=m-1;i++) if(m%2==0) break;
if(i>=m) return 1;
else return 0;}
第八章 函 数
87
例 29,写一个函数,求两个整数的最大公约数,
并用主函数调用这个函数,输出结果。
int gcd(int m,int n)
{int r; r=m%n;
while(r!=0) {m=n;n=r;r=m%n;}
return n;}
main( )
{int m,n,t; scanf(“%d,%d”,&m,&n);
if(m<n) {t=m;m=n;n=t;}
printf(“%d”,gcd(m,n));}
第八章 函 数
88
例 30,用递归的方法求 n!
1 n=0,1
n!=
n*(n-1)! n>1
float fac(int n)
{float 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;}
main()
{int n; float y; scanf(“%d”,&n);
y=fac(n);
printf(“%d!=%f”,n,y);}
第八章 函 数
89
解决方法:
1)用 Turbo C集成环境建立项目文件先编写一个扩展名为,.prj”的项目文件,内容是若干文件的列表,每行一个文件或者用空格隔开,要加上扩展名,无顺序问题。如:
file1.c fle2.c file3.c file4.c
再使用 TC菜单,project”-,project name”项打开存在的项目文件,然后编译、连接,或者直接按 ctrl+F9运行。则将多个文件编译连接生成一个可运行程序。
当然,多个文件中要有 1个文件中含有 main函数。
补充,如何运行一个多文件的程序第八章 函 数
90
解决方法:
2)用 #include 命令将 file2.c,file3.c,file4.c包含到 file1.c中,在
file1.c的开头加三行:
#include,file2.c”
#include,file3.c”
#include,file4.c”
如同标准函数的调用一样。利用 include头文件形式注明。
补充,如何运行一个多文件的程序