View
139
Download
2
Category
Preview:
DESCRIPTION
第 8 章 函数. 8.1 概述. 程序结构概念 : 1 )较大的程序应分为 若干个程序模块 ; 2 )每个模块实现单一的特定功能; 3 )由主函数开始执行,主函数调用子函数; 4 )子函数之间可以互相调用。. 说明 :. 1. 一个源文件可由一个或多个函数组成; 2. 一个 源文件 是一个 编译单位 ; 3. 一个 C 程序 由一个或多个 源文件 组成。 4. 从 main() 开始,调用其它函数后, 回到 main() 结束 ; 5. 不能嵌套定义,但可互相调用 。 6. 分类 : - PowerPoint PPT Presentation
Citation preview
程序结构概念 : 1 )较大的程序应分为若干个程序模块 ; 2 )每个模块实现单一的特定功能; 3 )由主函数开始执行,主函数调用子函数; 4 )子函数之间可以互相调用。
第 8 章 函数8.1 概述
1. 一个源文件可由一个或多个函数组成;2. 一个源文件是一个编译单位;3. 一个 C 程序由一个或多个源文件组成。4. 从 main() 开始,调用其它函数后, 回到 main() 结束 ;5. 不能嵌套定义,但可互相调用 。6. 分类 : 1 ) 标准函数 和 用户自定义函数 ; 2 ) 无参函数 和 有参函数 。
说明 :
1. 1. 无参函数的定义无参函数的定义 形式: 类型标识符 函数名 ( ) { 说明部分 语句 }
8.2 8.2 函数定义函数定义
例如:main( ){ int a=5,b=9 ; printf(" %d\n " , a*b*b ); }
说明:若不带回函数值,类型标识符也可以不写,无参函数一般用于完成指定的一组操作。
形式: 类型标识符 函数名 ( 形式参数表列 ) 形式参数说明 { 说明部分 语句 } 如: int max( x, y )
int x , y; { int z ; z=x>y ? x : y ; return ( z ) ; }
空函数定义形式: 类形标识符 函数名 ( ) { }作用:备以后扩充。
22 .有参函数的定义.有参函数的定义
形式参数 :形式参数 :定义函数时,括号中说明的变量名 ;实际参数 :实际参数 : 调用函数时,括号中给定的表达式 。
8.3 8.3 函数参数和函数的值函数参数和函数的值 8.3.1 形式参数和实际参数
}
main( ){ int a,b,c;
scanf ("%d,%d",&a,&b);c=max( a ,b );printf ("max is %d\n",c);
}max( x, y )int x,y;{ int z;
z=x>y?x:y;return (z);
main( ){ int a,b,c;
scanf ("%d,%d",&a,&b);c=max( a ,b );
max( x, y )int x,y;{ int z;
z=x>y?x:y;return (z);
printf ("max is %d\n",c);
形参说明
函数调用
int
1. 1. 定义函数时,形参定义函数时,形参不占不占内存单元 ;内存单元 ; 调用函数时,形参才调用函数时,形参才分配分配内存单元 ;内存单元 ; 调用结束后,形参所占内存单元被调用结束后,形参所占内存单元被释放释放。。 2. 2. 实参可以是常量、变量或表达式,但必须有确实参可以是常量、变量或表达式,但必须有确
切的值。切的值。 3. 3. 定义函数,必须指明形参类型。定义函数,必须指明形参类型。 4. 4. 实参与形参类型一致。实参与形参类型一致。 5. 5. 实参变量对形参变量的数据传递是“实参变量对形参变量的数据传递是“值传递值传递 ”, ”,
即即 单向单向 传递。传递。 6. 6. 可在“形参表列”中说明形参类型。可在“形参表列”中说明形参类型。 int max( int max( int int x , x , int int y ) y ) { { 函数体 函数体 } }
说明说明 ::
说明 :说明 : 1. 1. 通过 通过 return return 语句获得返回值 ;语句获得返回值 ; 2. 2. 定义函数时指定函数返回值的类型;不加类型说定义函数时指定函数返回值的类型;不加类型说
明明 的,按整型处理。的,按整型处理。 3. 3. 函数值的类型和 函数值的类型和 return return 语句中表达式的值不一语句中表达式的值不一 致时,以函数类型为准。致时,以函数类型为准。 4. 4. 被调用函数中没有 被调用函数中没有 return return 语句时,带回一个不语句时,带回一个不 确定的值。确定的值。 5. 5. 为了明确表示“不带回值”,可以用“ 为了明确表示“不带回值”,可以用“ voidvoid ” ” 定 定
义 “ 义 “ 无类型无类型 ”。 ”。
8.3.2 8.3.2 函数的返回值函数的返回值
看下面例子:main()
{float a,b;
int c;
scanf(“%f,%f”,&a,&b);
c=max(a,b);
printf(“Max is %d\n”,c);
}
max(float x,float y)
{float z;
z=x>y?x:y;
return (z)}运行结果:1.5,2.5
Max is 2
8.4.1 函数调用的一般形式 函数名(实参表列) 8. 4 8. 4 函数的调用函数的调用
说明 :1. 无参函数,括号不能省 ;2. 实参表列中,各实参与形参在 个数、顺序、 类型上一一对应,参数间用逗号分隔。注意:计算实参时的次序不同。为了提高程序计算实参时的次序不同。为了提高程序的通用性,一般都写清楚。的通用性,一般都写清楚。
int f(int a,int b) { int c; if (a>b) c=1; else if (a= =b) c=0; else c=-1; return (c); }
main( ){int i,j,p; scanf("%d%d",&i,&j); p=f(i,j); printf ("%d",p);}
5a 9b
5 9i j
函数调用结束
a,b 所占的存储单元被释放 5 9i j
函数调用发生
8.4.2 函数调用的方式调用函数的方式有三种: 函数语句、函数表达式、函数参数。 如: printf("******"); m=max(a,b)*20; printf("%d",max(a,b));
1. 被调函数必须存在;2.用 #include 命令包含有关库函数;3.被调用函数一般应在主调函数前定义 ,这样在主调函数中可以不对调用函数类型进行声明。否则在主调函数中必须对调用函数类型进行声明 (整型除外 );4. 如果在文件开头,已声明了被调函数类型,则主调函数中不必再作类型声明。
8.4.3 对被调用函数的声明和函数原型
main( ) {float add( ); float a,b,c; ..... }
float add(x,y) float x,y; {…}
例 1 : 被调用函数声明
float add(float,float);
函数定义
float add(float x,float y );
函数定义是指对函数功能的确立,包括指定函数名、函数值类型、形参个数及类型、函数体等。它是一个完整、独立的程序单位。
函数声明的作用是把函数名及函数类型、形参个数及类型通知编译系统,以便在调用该函数时系统按此进行对照检查。
main( ){ float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("%f\n",c); }
例 2 : float add(x,y) float x,y; { float z; z=x+y; return(z); }
在主调函数前定义
main( ) {......} char let(c1,c2) char c1,c2; {......} float f(x,y) float x,y; {......} int max(j,k) float j,k; {......}
已在文件开头声明
例 3 :char let( );float f( );int max( );
定义 let 函数定义 f 函数
定义 max 函数
int y(float x){ int z; if (x>0) z=1; else if (x<0) z=-1; else z=0; return(z); }
程序举例:例 1 :求下列函数的值
-1 x<0y=
1 x>00 x=0
main( ){ float a; int y(); scanf ("%f",&a); printf ("%d", y(a)); }
y(a)
5a
5x
float x;
scanf("%f",&x);
x
y(x)
例 2 .求 5 !、 16 !和 27 !float jiec(n)int n;{ float y=1; int i; for (i=1;i<=n;i++) y=y*i; return (y); } main( )
{ float a,b,c; a=jiec(5); b=jiec(16); c=jiec(27); printf ("%f,%f,%f\n",a,b,c); }
例 3 .编写函数,判定某数字是否在某正整数中,若在,打印 TRUE ,否则打印 FALSE , 输出结果在主函数中完成。 main( ) {int m,n; /* 判断数字 n 是否在数 m 中 / int among(int m,int n); scanf("%d %d",&m,&n); if(among(m,n)) printf("TRUE"); else printf("FALSE"); }
int among(m,n)int m,n; { int k,z=0; do {k=m%10; if (n==k) {z=1; break;} m=m/10; }while(m!=0); return(z); }
main(){int i,k=0; for(i=400;i<=499;i++) k=k+num(i); printf ("number=%d\n",k); }
例 4 .统计 400 ~ 499 这些数中 4 这个数字出现的次数,判一个数有几个数字 4 ,用函数实现 。num(x)int x;{ int y,k=0; while (x!=0) {y=x%10; if(y==4) k++; x=x/10; } return(k); }
wan(x)int x;{ int i,k=0; for (i=1;i<=x/2;i++) if (x%i==0) k=k+i; if (k==x) return (1); else return (0); }
例 5 . 找出 1000 之内的所有“完数”,判一个数是否为完数,用函数实现 。
main( ){ int i; for (i=1;i<1000;i++) if (wan(i)) printf ("%5d",i); printf ("\n"); }
#include "math.h"float area1(a,b,c)float a,b,c;{ float s,p; p=(a+b+c)/2; s=sqrt(p*(p-a)*(p-b)*(p-c)); return(s); }
float area2(d)float d;{ float s; s=3.14159*(d/2)*(d/2)/2; return(s); }
例 6 .求图形的面积 。
main( ){ float a,b,c,s; scanf ("%f%f%f",&a,&b,&c); s=area1(a,b,c)+area2(a)+area2(b)+area2(c); printf ("%f\n",s); }
不能嵌套定义函数,可以嵌套调用嵌套调用函数 。 main 函数 {
调用 a 函数
}
8.5 8.5 函数的嵌套调用函数的嵌套调用 a 函数 {
调用 b 函数
return(); }
b 函数 {
return(); }
int gcd(a,b)int a,b;{int r; do {r=a%b; a=b; b=r;} while (r!=0); return (a); }
例 .写两个函数,分别求两个整数的最大公约数和最小公倍数,用主函数调用这两个函数,并输出结果 。
main( ){ int x,y; scanf ("%d%d",&x,&y); printf ("%d\n",gcd(x,y)); printf ("%d\n",lcm(x,y)); }
int lcm(a,b)int a,b;{ int r; r=gcd(a,b); return(a*b/r); }
在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用 。
8.6 8.6 函数的递归调用函数的递归调用
递归问题的特点 : 1 . 把一个问题转化为一个新问题,新问题与原问题解法相同 ,只是所处理的对象有所不同,但它们只是有规律的递增或递减 。 2 . 必须有某个终止递归终止递归的条件。
例 1 . 用递归法求 n! 1 (n=1 或 n=0) n!= n*(n-1)! (n>1)
n!=n*(n-1)!
(n-1)!=(n-1)*(n-2)!
...
2!=2*1!
1!=1
main( ){ int n; float fac( ); float y; scanf ("%d",&n); if (n<0) printf ("n<0,data error\n"); else { y=fac(n); printf ("%d!=%15.0f",n,y); }}
float fac(n)int n;{ float f; if (n==1||n==0) f=1; else f=fac(n-1)*n; return(f); } 以 n=4 为例,执行过程如下:
main( )
{ ...
y=fac(4);
...
}
fac(n)
{ ...
f=fac(3)*4;
return(f);
}
fac(n)
{ ...
f=fac(2)*3;
return (f);
}
fac(n)
{ ...
f=fac(1)*2;
return (f);
}
fac(n)
{ ...
f=1;
return (f);
}
12624
例 3 .求 xn (n>0) 1 (n=0) xn = x (n=1) x*xn-1 (n>1)
main( ){ int x,n; float f( ); scanf ("%d%d",&x,&n); printf ("%d %d %f\n",x,n,f(x,n)); }
float f(int x,int n){ float z; if(n==0) z=1; else if (n==1) z=x; else z=x*f(x,n-1); return z; }
float f(x,n)int x,n;{ float z; if (n==0) z=1; else if (n==1) z=x; else z=x*f(x,n-1); return z; }
float f(x,n)int x,n;{ float z; if (n==0) z=1; else if (n==1) z=x; else z=x*f(x,n-1); return z; }
float f(x,n)int x,n;{ float z; if (n==0) z=1; else if (n==1) z=x; else z=x*f(x,n-1); return z; }
float f(x,n)int x,n;{ float z; if (n==0) z=1; else if (n==1) z=x; else z=x*f(x,n-1); return z; }
f(5,4)
5,3
5,2
5,1
z=5;
1 (n=0) pn(x)= x (n=1) (( 2n-1) · x · p n-1(x)-(n-1) · p n-2(x)) /n (n>1)
例 4 .用递归方法求 N阶勒让德多项式的值,递归公式 为 :
float pn(x,n) float x; int n; { float temp; if (n==0) temp=1; else if (n==1) temp=x; else temp=((2*n-1)*x*pn(x,n-1)-(n-1)*pn(x,n-2))/n; return (temp); }
main( ) { float pn(float,int), x,lyd; int n; scanf ("%d %f",&n,&x); lyd=pn(x,n); printf ("pn=%f\n",lyd); }
1 、 数组元素作函数实参 对应的形参为简单变量。8.7 8.7 数组作为函数参数数组作为函数参数
例 1 :求正整数数组 a[10] 中的素数 .main( ) { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); for(i=0;i<10;i++) if(sushu(a[i])) printf("%5d",a[i]); }
int sushu(x) 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); }
main( ) { int a[4][4],i,j; for(i=0;i<4;i++) for(j=0;j<4;j++) scanf("%d",&a[i][j]); for(i=0;i<4;i++) for(j=0;j<4;j++) if(sushu(a[i][j])) printf("%5d",a[i][j]);} printf("\n"); }
例 2 :求正整数数组 a[4][4]中的素数 .
int sushu(x) 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); }
main( ) { int a[4][4],i,j; for(i=0;i<4;i++) for(j=0;j<4;j++) {scanf("%d",&a[i][j]); if(sushu(a[i][j])) printf("%5d",a[i][j]);} printf("\n"); }
2 、数组名可作函数实参对应的形参为数组名说明 :
1. 数组名作函数参数,应在主调函数和被调函数中分别定义数组 ; 2. 实参数组与形参数组类型应一致 ; 3. 实参数组与形参数组大小可以一致也可以不一致 。形参数组可以不指定大小。 4. 数组名作函数参数时,是 “ 地址传送地址传送 ”。
ab
a[0]a[1] a[3]a[4]a[5]a[6] a[7]a[8]a[9]a[2]
b[1] b[3]b[4]b[5]b[6] b[7]b[8]b[9]b[2]b[0]
float average(array)float array[10];{ int i; float aver,sum=0; for (i=0;i<10;i++) sum=sum+array[i]; aver=sum/10; return(aver); }
main( ) { float score[10],aver,a; float average(float); int i; for (i=0;i<10;i++) { scanf ("%f",&a); score[i]=a;} aver=average(score); printf (" %5.2f",aver); }
例 3: 数组 score 存放 10 个学生成绩,求平均成绩 。
scorearray
void sort(array,n)int array[ ],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; } }
例 4 :用选择法对数组中 10 个整数按升序排序 。main(){ int a[10],i; void sort( ); 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"); }
12 3 45 -7 6 17 4 9 2 77 aarray -7 2 3 4 6 9 12 17 45 77
例 5 :用冒泡法对数组中 10 个整数按升序排序 。冒泡法的思路是 :将相邻两个数比较 ,将小的调到前头 . 依此类推经过 j 轮比较每轮比较 n-j-1 次
for(j=0;j<n-1;j++)for(i=0;i<n-j-1;i++)if(a[i]>a[i+1])
{ t=a[i]; a[i]=a[i+1]; a[i+1]=t; }
5318609-2214
53
5
1
808
9
21
6
9-2
214
315608-294
135660
8994
第一轮 :
(j=0)
第二轮 :
(j=1)
比较 9次 比较 8次
21
8-2
9
void sort(a,n)int a [ ],n; { int i,j, t; for (j=0;j<n-1;j++) for (i=0;i<n-j-1;i++) if (a[i]>a[i+1]) { t=a[i]; a[i]=a[i+1]; a[i+1]=t; } }
main(){ int array[10],i; void sort( ); for (i=0;i<10;i++) scanf ("%d",&array[i]); sort(array,10); for (i=0;i<10;i++) printf ("%5d",array[i]); printf ("\n"); }
5 3 1 8 6 0 9 -2 21 4 aarray -2 0 1 3 4 5 6 8 9 21
例 6 .产生 15 个 [10,90]上的数放入 a 数组中,找出其中 的素数放入 b 数组,并求 b 数组中元素的和 。要求 : 1. 求素数用函数 sushu( ) 完成 2. 求素数的和用函数 sum( ) 完成
11 13 10 87 43 41 79 77 76 51 53 63 33 36 47 a
b 11 13 43 41 79 53 47
k=01234567
#include "stdlib.h" main() {int a[15],b[15]; int i,m=0; for(i=0;i<15;i++) { a[i]=random(81)+10; if(sushu(a[i])==1) {b[m]=a[i]; m++;} } printf("the array A is:\n"); for(i=0;i<15;i++) printf("%5d",a[i]); printf("\n"); printf("the array B is:\n"); for(i=0;i<m;i++) printf("%5d",b[i]); printf("\n"); printf("\nthe sum of array B is:%d\n",sum(b,m)); }
方法一:
sushu(int k) {int n,x; x=1; if(k==1)x=0; for(n=2;n<k/2;n++) if(k%n==0) x=0; return(x); }
int sum(b,n)int b[ ],n; {int i,s=0; for(i=0;i<n;i++) s=s+b[i]; return(s); }
#include ”time.h"#include "stdlib.h" main() {int a[15],b[15],i,m; randomize(); for(i=0;i<15;i++) a[i]=random(81)+10; m=sushu(a,b); for(i=0;i<m;i++) printf("%4d",b[i]); printf("\n"); printf("sum=%d\n", sum(b,m)); }
方法二:
int sushu(a,b) int a[ ],b[ ]; { int i,j,k=0; for(i=0;i<15;i++) {for(j=2;j<=a[i]-1;j++) if(a[i]%j==0) break; if(j>a[i]-1) {b[k]=a[i];k++;} } return(k); }
int sum(b,n)int b[ ],n; {int i,s=0; for(i=0;i<n;i++) s=s+b[i]; return(s); }
3 、用多维数组作函数参数 1. 用多维数组元素作函数实参,这点与前述相同。2. 用多维数组名作函数实参和形参,在被调用函数中对形参数组定义时可以指定每一维的大小, 也可以省略第一维的大小说明。 如: int a[3][10];
int a[][10];
二者都合法完全等价。但是不能把第二维以及其它高维的大小说明省略。
max_value(array)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); }
例 8 .有一个 3×4 的矩阵,求其中的最大元素 。main( ){ static int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}}; printf ("max=%d\n",max_value(a)); }
max_value(array)int array[12];{ int i,max; max=array[0]; for(i=0;i<12;i++) if(array[i]>max) max=array[i]; return(max); }
例 9: 求 3×3 矩阵转置 23 34 7
2 56 17
21 4 36
23 34 7
2
56
17
21
4
36交换 a[i][j] 与 a[j][i] 的值
void turn(array) int array[][3]; { int i,j,k; for (i=0;i<3;i++) for (j=0;j<i;j++) { k=array[i][j]; array[i][j]=array[j][i]; array[j][i]=k; } }
main(){ static int a[3][3]={{1,3,5}, {2,4,6},{15,17,34}}; int i,j; turn(a); for (i=0;i<3;i++) { for (j=0;j<3;j++) printf ("%5d",a[i][j]); printf ("\n"); } }
8.8.1 局部变量 只在函数内有效的变量 。
a , b, c 有效
8.8 8.8 局部变量和全局变量局部变量和全局变量
char f2(x,y) int x,y;{ int i,j; ...... }
x ,y ,i ,j 有效m ,n 有效
main ( ){ int m,n; ...... }
float f1(a)int a;{ int b,c; ...... }
说明 : 1. 每个函数中定义的变量,只在定义它的函数中有效 ; 2. 不同函数可以使用相同名字的变量,但意义不同 ; 3. 形式参数是局部变量 ; 4. 可以在复合语句中定义变量,但它们只在本复合语 句中有效 。
main ( ) { int a,b; ...... {int c; c=a+b; ...... } ...... }
c 的有效范围 a , b 的有效范围
1. 外部变量 :在函数之外定义的变量;2. 外部变量是全局变量 ;3. 作用范围: 从定义变量的位置开始到本源文件结束
8.8.2 全局变量
4. 说明 : (1) 作用 :提供一种函数间数据联系与共享的方法 ; (2) 尽量不用全局变量 ; 1 ) 占内存 2 ) 藕合性强 3 )维护性差 (3) 用 extern 实现先引用 ,后定义 。 (4) 外部变量与局部变量同名时,局部变量屏蔽外 部变量 。
int p=1,q=5; float f1(a) int a; { int b,c; ..… } char c1,c2; char f2(x,y) int x,y; { int i,j; ..… } main ( ) { int m,n; ...... }
c1,c2 的作用范围
p,q 的作用范围
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;
例 1 :
外部变量说明
外部变量定义
int a=3,b=5; /*a,b 为外部变量 */ max(a,b) int a,b; /*a,b 为局部变量 */ { int c; c=a>b? a : b; return(c); } main( ) { int a=8; /*a 为局部变量 */ printf ("%d",max(a,b)); }
形参 a,b作用范围
局部变量 a 作用范围全局变量 b 作用范围
例 2:
例 3 :在一维数组中存放 10 个学生的成绩,写一个函数,求出平均分,最高分和最低分。分析 : 1. 平均分由函数值带回 2. 设max,min 为全局变量
score 10 max min
array n max min
main( )
average( )
float max,min;float average(array,n)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); }
main( ) { float ave,score[10],x; int i; for (i=0;i<10;i++) { scanf ("%f",&x);score[i]=x; } ave=average(score,10); printf ("%6.2f,%6.2f,%6.2f\n",max,min,ave); }
void average(array,n,b) float array[],b[];int n; { int i; float aver; b[0]=b[1]=b[2]=array[0]; for (i=1;i<n;i++) { if (array[i]>b[0]) b[0]=array[i]; else if (array[i]<b[1]) b[1]=array[i]; b[2]=b[2]+array[i]; } b[2]=b[2]/n; }
不用全局变量 , 利用数组名作参数 ,从而达到 "双向 "传送数值的目的 .
main( ) { float y[3],score[10],x; int i; for (i=0;i<10;i++) { scanf ("%f",&x);score[i]=x;} average(score,10,y); printf ("max=%6.2f,min=%6.2f,ave=%6.2f\n",y[0],y[1],y[2]); }
8.9.1 动态存储方式与静态存储方式 1.静态存储变量 :程序运行期间分配固定的存储空间,存放全局变量或静态局部变量。 2.动态存储变量 :根据需要动态分配存储空间 ,存放 : 1 ) 函数形参变量 ; 2) 局部变量(未加 static 说明的); 3 ) 函数调用时的现场保护和返回地址等 。
8.9 变量的存储类别
调用时,系统自动给变量分配存储单元,称这类局部变量为自动变量( auto),其特点: 1 ) 函数调用后,值不予保留,即释放存储空间 。 2) 再次调用时,原值不能引用 。8.9.3 用 static声明局部变量 1 ) 函数调用后保留原值,即不释放所占存储空间; 2) 再次调用时,原值在本函数内仍可使用 。
8.9.2 auto 变量 (局部变量的存储方式)
int f(a)int a;{ int b=0; static int c=3; b=b+1;c=c+1 return(a+b+c); }
例 1 :main( ){ int a=2,i; for (i=0;i<3;i++) printf("%d",f(a)); }
第几次调用 调用时初值 调用结束时的值b bc c a+b+c
123
000
345
111
456
789
局部静态变量
main( ){ int i; for(i=1;i<=5;i++) printf("%d!=%5.1f\n", i,fac(i)); }
float fac(int n){ static float f=1; f=f*n; return(f); }
例 2:
1!= 1.0
2!= 2.0
3!= 6.0
4!= 24.0
5!=120.0
1. 局部静态变量在静态存储区内分配存储单元, 程序整个运行期间不释放 ;2. 局部静态变量编译时赋初值一次,以后每次调用不再重新赋初值,而是保留上次函数调用结束时的值 ;3. 局部静态变量定义时不赋初值,编译时自动赋初值 0 ;4. 定义全局变量和局部静态变量时,才能对数组初始化 ;5. 不能被其它函数引用 。
说明 :
8.9.4 register 8.9.4 register 变量变量一般情况下,变量的值是放在内存中的,若要用一般情况下,变量的值是放在内存中的,若要用到该变量的值时,由控制器发出指令将内存中到该变量的值时,由控制器发出指令将内存中的值送到运算器中进行运算(如下示意图)。的值送到运算器中进行运算(如下示意图)。
运算器
内 存存数 取数
如果有些变量使用比较频繁,如果有些变量使用比较频繁,为了提高执行效率,为了提高执行效率, CC 语言允语言允许将局部变量的值放在许将局部变量的值放在 CPUCPU 中中的寄存器中,需要时直接从寄的寄存器中,需要时直接从寄存器中取数运算,不必再到内存器中取数运算,不必再到内存中存取。这种变量叫做“寄存中存取。这种变量叫做“寄存器变量”,用关键字存器变量”,用关键字 registerregister作声明。看下例(作声明。看下例( PP175175 例例 8.18.199 )) ::
int fac(int n){register int i,f=1; /* 定义寄存器变量 */ for (i=1;i<=n;i++) f=f*i; return (f);}main(){int i; for (i=1;i<=5;i++) printf(“%d!=%d\n”,i,fac(i));}
8.9.5 用 extern声明外部变量外部变量(即全局变量),是在函数的外部定义的,外部变量(即全局变量),是在函数的外部定义的,其作用域从变量定义处开始,到本程序文件的末尾。其作用域从变量定义处开始,到本程序文件的末尾。 1. 在一个文件内声明外部变量:在函数外部定义变量。 2. 在多个文件的程序中声明外部变量 , 需要用 extern 作说明 。看下例:
file1.c
int a;main( ){ int m,k; k=power(m); printf("%d",k);}
file2.cextern int a;power(n)int n;{ int i,y=1; for(i=1;i<=n;i++) y=y*a; return(y);}
8.9.6 用 static声明外部变量 1) 在函数外部用 static定义 2) 只能被本文件中的函数引用file1.c
static int a;main( ){ ..... k=power(m); ....}
file2.c
extern int a;power(n)int n;{ int i,y=1; for(i=1;i<=n;i++) y=y*a; return(y);}
这样引用是错误的
这种加上这种加上 staticstatic 声明、只能用于本文件的外声明、只能用于本文件的外部变量(全局变量)称为静态外部变量。部变量(全局变量)称为静态外部变量。
1. 1. 按作用域分为局部变量和全局变量按作用域分为局部变量和全局变量 存储类别:存储类别: 动态局部变量(离开参数,值就消失)动态局部变量(离开参数,值就消失)局部变量 静态局部变量(离开参数,值仍保留)局部变量 静态局部变量(离开参数,值仍保留) 寄存器变量 (离开参数,值就消失)寄存器变量 (离开参数,值就消失) (形参可以定义为自动变量或寄存器变量)(形参可以定义为自动变量或寄存器变量)全局变量 静态外部变量(只限本文件引用)全局变量 静态外部变量(只限本文件引用) 外部变量(即非静态外部变量,允许其它文件引外部变量(即非静态外部变量,允许其它文件引用)用)
8.9.8 存储类别小结
静态存储是程序整个运行期间都存在 ;静态存储是程序整个运行期间都存在 ;动态存储则是在调用函数时临时分配单元 。动态存储则是在调用函数时临时分配单元 。 自动变量 (本函数内有效)自动变量 (本函数内有效)动态存储动态存储 寄存器变量(本函数内有效)寄存器变量(本函数内有效)形式参数形式参数
静态局部变量(函数内有效)静态局部变量(函数内有效)静态存储静态存储 静态外部变量(本文件内有效)静态外部变量(本文件内有效)外部变量(其它文件可引用)外部变量(其它文件可引用)
2. 2. 按存在时间分为动态存储和静态存储两种类型按存在时间分为动态存储和静态存储两种类型
3. 3. 按变量存放位置分为 :按变量存放位置分为 : 静态局部变量静态局部变量内存中静态存储区 静态外部变量(函数外部静态变量)内存中静态存储区 静态外部变量(函数外部静态变量) 外部变量(可为其它文件引用)外部变量(可为其它文件引用)内存中动态存储区 : 自动变量和形式参数内存中动态存储区 : 自动变量和形式参数CPUCPU 中的寄存器 : 寄存器变量中的寄存器 : 寄存器变量
4 . 4 . 关于关于作用域作用域和和生存期生存期的概念的概念11 )自动变量和寄存器变量的作用域和存在性)自动变量和寄存器变量的作用域和存在性一致一致 , , 离开函数后,值不存在,不能被引用 。离开函数后,值不存在,不能被引用 。22)静态外部变量和外部变量的作用域和存在性也)静态外部变量和外部变量的作用域和存在性也一一致致 , , 离开函数后,变量值仍存在,可被引用 。离开函数后,变量值仍存在,可被引用 。33 )静态局部变量的作用域和存在性)静态局部变量的作用域和存在性不一致不一致 ,, 离开函离开函数后,变量值存在,但不能被引用 。数后,变量值存在,但不能被引用 。5. static5. static对局部变量和全局变量的作用不同对局部变量和全局变量的作用不同11 )对局部变量 :使动态存储方式改变为静态存储 ;)对局部变量 :使动态存储方式改变为静态存储 ;22)对全局变量 :使变量局部化,但仍为静态存储 ;)对全局变量 :使变量局部化,但仍为静态存储 ;33 )凡有 )凡有 static static 说明的,其作用域都是局限的。说明的,其作用域都是局限的。
根据函数能否被其它源文件调用,将函数分根据函数能否被其它源文件调用,将函数分为内部函数和外部函数。为内部函数和外部函数。11 、、内部函数内部函数 :如果一个函数只能被本文件 :如果一个函数只能被本文件其它函数所调用其它函数所调用 ,,它称为内部函数。它称为内部函数。 staticstatic 类型标识符 函数名(形参表)类型标识符 函数名(形参表)22 、 、 外部函数 :外部函数 : [ [ externextern ] ] 类型标识符 函数名(形参表)类型标识符 函数名(形参表)
8.10 8.10 内部函数和外部函数内部函数和外部函数
8.11 8.11 如何运行一个多文件的程序如何运行一个多文件的程序1 、用 Turbo C集成环境建立项目文件2 、用 #include 命令
Recommended