?C是模块化程序设计语言
源程序文件1
预编译命令
说明部分 执行部分
函数1 函数n
源程序文件i 源程序文件n
C程序
C程序结构
?C是 函数式 语言
?必须有且只能有一个名为 main的主函数
?C程序的执行总是 从 main函数开始,在 main中结束
?程序中函数 都是平行的,不能嵌套定义,可以 嵌套 调用
第八章 函数
main()
{ int a,b;
int c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf("Max is %d\n",c);
}
max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例如下面是一个 C源程序,它由两个函数组成,
即 main 与 max 函数
main()
{ int a,b;
int 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);
}
函数分类
1、标准函数(库函数:由系统提供 ),
例如,printf 函数, sqrt函数,
2、用户自定义函数, 例如,上面程序中的
max 函数
max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
函数的定义
?一般格式
函数体
函数类型 函数名 ( 形参类型说明表 )
{
说明部分
语句部分
}
例 有参函数(现代风格)
int max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例 有参函数(现代风格)
int max(int x,y)
{ int z;
z=x>y?x:y;
return(z);
}
例 空函数
dummy( )
{ }
函数体为空
例 无参函数
printstar( )
{ printf(“**********\n”); }
省略时为 int 类型
void 类型是无返回
值
形参
§ 8.3 函数的返回值
返回语句形式,
return(表达式 );
或 return 表达式 ;
或 [return;]
功能,使程序控制从被调用
函数返回到调用函数中,同时把返值带给调用
函数
函数结束标志,遇到 return 语句 或执行到
最后一个 }
main()
{ int c,a=1,b=2;
c=add(a,b);
printf(,c= %d,,c);
}
int add(int x,int y)
{ int z;
z=x+y ;
return(z);
} 演示
printstar()
{ printf("**********");
}
main()
{ int a;
a=printstar();
printf("%d",a);
}
例 函数带回不确定值
输出,10
void printstar()
{ printf("**********");
}
main()
{ int a;
a=printstar();
printf("%d",a);
}
编译错误!
演示
例 函数返回值类型转换
main()
{ 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=x>y?x:y;
return(z);
}
运行情况,
输入,2.5,3.5
调用函数
c=max(2.5,3.5)
返回值( return(3.5))
输出,Max is 3
演示
§ 8.4 函数的调用
调用形式
函数名 (实参 1,实参 2,…,实参 k);
对照函数定义形式
类型 函数名 (类型 1 形参 1,类型 2 形参 2,….)
{……}
说明,实参与形参 个数相等,
类型一致, 按顺序一一对应
调用方式
函数语句,
例 printstar();
printf(“Hello,World!\n”);
函数表达式,
例 m=max(a,b)*2;
函数参数,
例 printf(“%d”,max(a,b));
m=max(a,max(b,c));
main()
{ int c,a=1,b=2;
c=add(a,b)*2;
printf(,c= %d,,c);
}
int add(int x,int y)
{ int z;
z=x+y ;
return(z);
}
printstar()
{ printf("**********");
}
main()
{ printstar();
}
函数声明
1、什么是函数声明?
函数声明的一般形式,
类型 函数名 (形参类型 [形参名 ],….,) ;
例如,int max( int x,in y) ;
或 float add(float,float ) ;
2、函数声明的作用是什么?
告诉编译系统 函数类型、参数个数及类型,以便检
验
3、函数声明放在什么位置?
放在定义变量的位置,即 程序的数据说明部分
4、什么情况可以省略函数声明?
1) 若函数返值是 char或 int型, 系统自动按 int型处
理
2) 被调用函数定义出现在主调函数之前
3) 在所有函数定义之前,在函数外部做了函数声
明。
例 函数声明举例
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);
}
int型函数可不作函数说明
/*ch7_5.c*/
float add(float x,float y)
{ float z;
z=x+y;
return(z);
}
main()
{ float a,b,c;
scanf("%f,%f",&a,&b);
c=add(a,b);
printf("sum is %f",c);
}
被调函数出现在主调函数
之前,不必函数说明
/*ch8_5.c*/
main()
{ float add(float,float); /*function declaration*/
float a,b,c;
scanf("%f,%f",&a,&b);
c=add(a,b);
printf("sum is %f",c);
}
float add(float x,float y)
{ float z;
z=x+y;
return(z);
}
float add();
#i clude <stdio.h>
foat m x(float x,float y);
main()
{ float a,b;
int c;
scanf("%f,%f",&a,&b);
c=max(a,b);
printf("Max is %d\n",c);
}
foat max(float x,float y)
{ float z;
z=x>y?x:y;
return(z);
}
在函数外面做了
声明,
所有调用函数
不必再声明
§ 8.5 函数参数及其传递方式
?形参与实参
?形式参数:定义函数时函数名后面括号中的变量名
?实际参数:调用函数时函数名后面括号中的表达式
c=max(a,b); ( main 函数)
( max 函数) max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例 比较两个数并输出大者 main()
{ int a,b,c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf("Max is %d",c);
}
max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
形参
实参
?说明,
?实参必须有确定的值
?形参必须指定类型
?形参与实参 类型一致,个数相同
?形参在函数被调用前不占内存 ;函数调用
时为形参分配内存;调用结束,内存释放
§ 8.5 函数参数及其传递方式
形参与实参
例 计算 x的立方
#include <stdio.h>
float c(float x)
{ return(x*x*x);
}
main()
{ float a,p;
printf("Please input value of a:");
scanf("%f",&a);
p=c(a);
printf(”Cube of %.4f is %.4f\n",a,p);
}
x
a
p
××
××
1.2
1.2
1.728
内存的变化情况
运行情况,
Please input value of a,1.2
?参数传递方式
?值传递 方式
?方式:函数调用时,为形参分配单元,并将实参
的值 复制 到形参中;调用结束,形参单元被释
放,实参单元仍保留并维持原值
?特点,
?形参与实参占用 不同 的内存单元
?单向 传递
?实参可以是常量、变量或表达式,但必须
有确定的值。
7 11 x,y,调用前,
调用结束,7 11 x,y,
例 交换两个数
#include <stdio.h>
main()
{ int x=7,y=11;
printf("x=%d,\ty=%d\n",x,y);
printf("swapped:\n");
swap(x,y);
printf("x=%d,\ty=%d\n",x,y);
}
swap(int a,int b)
{ int temp;
temp=a; a=b; b=temp;
}
调用,
7 11 a,b,
7 11 x,y,
swap,7 11 x,y,
11 7 a,b,
temp
运行,
x=7 y=11
swapped,x=7 y=11
#include <stdio.h>
long sum(int a,int b);
long factorial(int n);
main()
{ int n1,n2;
long a;
scanf("%d,%d",&n1,&n2);
a=sum(n1,n2);
printf("a=%1d",a);
}
long sum(int a,int b)
{
long c1,c2;
c1=factorial(a);
c2=factorial(b);
return(c1+c2);
}
long factorial(int n)
{ long rtn=1;
int i;
for(i=1;i<=n;i++)
rtn*=i;
return(rtn);
}
long sum(int a,int b);
long factorial(int n);
文件包含编译预处理命令
函数类型说明
函数定义
函数调用
函数调用
函数返回值
形参
实参
§ 8.6 函数的嵌套与递归调用
?嵌套调用
C规定,函数定义不可嵌套,但 可以嵌套调用 函数
main( )
调用函数 a
结束
a函数 b函数
调用函数 b
? ? ? ?
?
? ? ?
?
函数嵌套
main()
{ float add( float,float ) ;
int a=2,b=3,c ;
c=add(a,b);
printf(,c= %d,,c);
float add(float x,float y)
{ float z ;
z=x+y ;
return(z) ;
}
}
嵌套调用
例 求三个数中最大数和最小数的差值
#include <stdio.h>
int dif(int x,int y,int z);
int max(int x,int y,int z);
int min(int x,int y,int z);
void main()
{ int a,b,c,d;
scanf("%d%d%d",&a,&b,&c);
d=dif(a,b,c);
printf("Max-Min=%d\n",d);
}
int dif(int x,int y,int z)
{ return max(x,y,z)-min(x,y,z); }
int max(int x,int y,int z)
{ int r;
r=x>y?x:y;
return(r>z?r:z);
}
int min(int x,int y,int z)
{ int r;
r=x<y?x:y;
return(r<z?r:z);
} main( )
调用函数 dif
输出
结束
dif函数 max函数
调用函数 max
调用函数 min min函数
作业,186页 8.1, 8.3
下次实验:实验十五( 写实验报告 )
实验项目,函数的定义和调用、变量
的存储属性
实验目的和实验内容按实验十五写
实验报告中除实验十五外,自己任选
有关函数题目写上。
关于字符型数组结尾中标识 ‘ \0? 问题
在下面情况,系统会自动在数组结尾加‘ \0?
1,char ch[6]=“abcde” 其中 ch[5]=?\0?
2,char ch[10]; 如果输入 5个字符,则第六元
scanf(“%s”,ch); 素 ch[5]=?\0 ? 而 ch[6]—ch[9]
的值不确定
或 char ch[10];
gets(ch);
注意 与 ch[10]=“abcde” ; 这时 ch[5]--ch[9] 都是‘ \0? 的区别
3,char ch[5]={?a?,?b?,?c?}; ch[3 ]=?\0?, ch[4 ]=?\0?
141页 7.8题
找出一个二维数组中的鞍点,即该位置上的元素在该行
上最大,在该列上最小。也可能没有鞍点。例如 下面的
3行 4列数组,
1 2 3 4
4
3
5 3 6
7 6 5
位于第 0行
,第 3列的
4就是一个
鞍点。
2 4 90 7
3
9
4 5 8
3 2 1
而二维数
组中无鞍
点。
定义矩阵的行 N,列 M
for(i=0;i<N;i++)
for(j=0;j<M;j++)
输入矩阵元素
for(i=0;i<N;i++)
max=a[i][0]
for(j=0;j<M;j++)
a[i][j]>max
T F
max=a[i][j],maxj=j
for(k=0,flag1=1; k<N && flag1 ; k++)
max>a[k][maxj]
T F
flag1=0
flag1=1
T F
输出鞍点的行、列号及其值
flag2=0
T F
输出“矩阵中无鞍点”
flag2=0
flag2=1
c程序,
#define N 3
#define M 4
main()
{ int i,j,k,flag1,flag2,a[N][M],max,maxi,maxj;
for(i=0;i<N;i++)
{printf(“第 %d行?\n”,i);
for(j=0;j<M;j++)
scanf(“%d”,&a[i][j]);
}
for(i=0; i<N; i++)
{for(j=0;j<M;j++)
printf(“%d”,a[i][j]);
printf(“\n”);
}
flag2=0;
for( i=0; i<N; i++)
{max=a[i][0];
for(j=0;j<M;j++)
if(a[i][j]>max) { max=a[i][j] ; maxj=j ;}
for(k=0,flag1=1; k<N && flag1 ; k++)
if(max>a[k][maxj])
flag1=0;
if(flag1)
{printf(“\n 第 %d行,第 %d列的 %d是鞍点 \n”,i,maxj,max);
flag2=1;
}
if(!flag2)
printf(“\n 数组中无鞍点 ! \n,);
}
}
本次课内容,
一、函数的递归调用
二、数组作为函数的参数
三、局部变量和全局变量
四、变量的存储类型
*五、内部函数和外部函数
*六、如何运行一个多文件的程序
自学
首先把上次课的内容复习一下
1、函数的定义形式
类型标识符 函数名 ([形式参数表 ])
{声明部分
语句
}
例如,float max(float x,float y)
{float z;
z=x>y?x:y;
return(t);
}
2、函数的调用
函数名 (实参表 ) ; 例如在主调函数中 c=max(a,b);
函数首部
结尾没有分号
3、形参与实参
定义函数中的参数为形参,形参一定是变量。
调用函数使用的参数为实参,实参可以是变量、
常量或表达式。
4、函数通过 return 语句返回值。
5、在主调函数中对被调函数的声明方式,
函数定义部分 + ;
例如,
main()
{float max(int x,int y) ;
……,
}
函数声明
main()
{
??
d=f1(n,m);
??
}
int f1(int a,int b)
{
??
c=f2(a,b);
??
}
int f2(int x,int y)
{
??
}
1
2
3
在一个源程序文件中所有函数的定义都是并列的,例如,
6、函数的位置
函数不能嵌套定义,也不能交叉定义。
例如下面的嵌套定义是错误的
main()
{
???
int f(int a,int b)
{
???
}
}
下面的交叉定义也是错误的
main()
{ …….,
int f(int a,int b)
{ ………
………,
}
}
主调
函数
被调
函数
主调
函数
被调
函数
7、函数的嵌套调用
例如,
main()
{ ……
s=f1(a,b);
………
}
f1(int n,int m)
{ …….,
y=f2(c);
……,
}
f2(int x )
{
……
}
8.6 函数的递归调用
定义:函数直接或间接的调用自身叫函数的递归调用
f( )
调 f 调 f2 调 f1
f1( ) f2( )
int f(int x)
{ int y,z;
……
z=f(y);
……,
return(2*z);
}
int f1(int x)
{ int y,z;
……
z=f2(y);
……,
return(2*z);
}
int f2(int t)
{ int a,c;
……
c=f1(a);
……,
return(3+c);
}
一个问题要采用递归方法来解决时
必须符合以下三个条件,
1、可以把一个问题转化为一个新问题,而这个新问
题的解决方法仍与原问题的解法相同 ;只是所处
理的对象有所不同,但他们只是有规律的递增或
递减,
3、必须要有一个明确的结束递归的条件,否则
递归将无止境地进行下去,也就是说必须要有
某个终止递归的条件,
2、可以通过转化过程使问题得到解决,
例 求 n的阶乘 #include <stdio.h> int fac(int n)
{ int f;
if(n<0) printf("n<0,data error!");
else if(n= =0 ) f=1;
else f = fac(n-1)*n;
return(f);
}
main()
{ int n,y;
printf("Input a integer number:");
scanf("%d",&n);
y=fac(n);
printf("%d! =%5d",n,y);
}
设 f(n)=n× (n-1)× … × 2× 1
则有,
? ?
?
> - ×
= =
) 0 ( )! 1 (
) 0 ( 1 !
n n n
n n
f(n)=
1 (n=0)
n × f(n-1) (n>0)
上面递归程序的执行过程如下:设 n=4
…,
y=fac(4)
….,
主函数
…
y=fac(3)*4
fac=24
….,
…
y=fac(2)*3
fac=6
……
…
y=fac(1)*2
fac=2
……
…
y=fac(0)*1
fac=1
……
n,4 n,3 n,2 n,1
fac=24 fac=6 fac=2 fac=1
…
y=1
fac=1
…
n,0
fac=1
本次调用结束时
返回值
形参
§ 8.7 数组作为函数的参数
数组作为函数参数分两种情况:即 数组元素 作函数参数和 数
组名 作函数的参数。
数组元素作函数参数时和一般变量作函数参数是一样的,是值传递 。
数组名(是数组存储区的首地址)作函数参数时与数组元素作参
数不同,是地址传递 。分别介绍如下,
1、数组元素作函数实参
传递方式是单向的,值传递。例如,
main() int sum(int x)
{int sum(int x); {int y;
int i,s=0,a[5]={1,2,3,4,5}; y=x*10;
for (i=0;i<10;i++) return(y);
s=s+sum(a[i]); }
printf(“s=%d\n”,s); 输出结果,( s=10+20+30+40+50 )
} s=150
2,数组名作函数参数
这时实参是数组名,因为数组名是数组存储区的首地址,
所以把数组名传递给形参的过程,就是把数组存储区的
首地址传给形参。这种传递方式称为,地址传递
要求:实参和形参分别定义数组,且类型应一致,形参
数组大小 (多维数组第一维 )可不指定。
形参数组名实际是 地址变量。 (因为用来存放数组存储
区首地址)
这时实参和形参对应同一个数组存储区,所以传递是双
向的。通过以下的例子来进一步理解。
例 1,求学生的平均成绩
#include <stdio.h>
float average(int stu[10],int n);
main()
{ int score[10],i;
float av;
printf("Input 10 scores,\n");
for( i=0; i<10; i++ )
scanf("%d",&score[i]);
av=average(score,10);
printf("Average is,%.2f",av);
}
float average(int stu[10],int n)
{ int i;
float av,total=0;
for( i=0; i<n; i++ )
total += stu[i];
av = total/n;
return av;
}
实参用数组名
形参用数组定义,?int stu[ ]
,
,
2
1
0
9
score
56
23
12
…,
…,
88
stu
例 2,数组排序 ---选择排序
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[i];
array[i]=array[k];
array[k]=t;
}
}
}
main()
{ int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
0
1
2
3
4
5
6
7
8
9
a 49
68
57
32
9
99
27
13
76
88
array
首地址
2000
0
1
2
3
4
5
6
7
8
9
a 9
13
27
32
49
57
68
76
88
99
array
首地址
2000
例 3,求二维数组中最大元素值
a00 a01 a02 a03
a10 a10 a12 a13
a20 a21 a22 a23
int max_value(int array[3][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);
}
main()
{ 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));
}
多维形参数组第一维维数
可省略,第二维必须相同
? int array[][4]
1 3 5 7
2 4 6 8
15 17 34 12
§ 8.8 局部变量和全局变量
一、什么是 局部变量?
在一个函数内部定义的变量称为局部变量。 局部变
量的有效范围是所在定义的函数内,也就是说只有在
本函数内才能使用他们 。在此函数以外是不能使用这
些变量的。例如:在下面源程序中
float f1(int a)
{int b,c;
………
}
char f2(int x,int y)
{ int b,c ;
……
}
a,b,c的有
效范围
x,y,b,c的
有效范围
main()
{int m,n;
………
}
m,n的有
效范围
说明,
1、主函数中定义的变量也属于局部变量,例如上面的变量 m,n 。
也只能在主函数内使用。
2、不同函数中可以使用相同名字的变量,在内存中占有不同的存
储单元。例如上面的函数 f1 和 f2中均使用局部变量 b 和 c 。
3,形参也是局部变量。
4、在一个函数内部,也可以在复合语句中定义变量,这些变量只
能在本复合语句中有效,这种复合语句称为“分程序”或“程序
块”。例如,
main()
{ int a,b ;
………….,
{ int c;
c=a+b;
……,
}
…….,
}
C的有效范
围
a,b的有效
范围
例 不同函数中同名变量
main()
{ int a,b;
a=3;
b=4;
printf("main:a=%d,b=%d\n",a,b);
sub();
printf("main:a=%d,b=%d\n",a,b);
}
sub()
{ int a,b;
a=6;
b=7;
printf("sub:a=%d,b=%d\n",a,b);
}
例 不同函数中同名变量
main()
{ int a,b;
a=3;
b=4;
printf("main:a=%d,b=%d\n",a,b);
sub();
printf("main:a=%d,b=%d\n",a,b);
}
sub()
{ int a,b;
a=6;
b=7;
printf("sub:a=%d,b=%d\n",a,b);
}
运行结果,
main:a=3,b=4
sub:a=6,b=7
main:a=3,b=4
地址
地址
3 4
6 7
函数 main中的变量 a,b的存储
单元
2000 2050
a b
b a
3100 3500
函数 sub中的变量 a,b的存储
单元
地址
地址
二、什么是 全局变量?
定义在函数外部的变量称为
全局变量,全局变量可以为本
文件中其它函数所使用。它的
有效范围为从定义变量的位置
开始到本源文件结束。例如,
int p=1,q=5;
float f1(int a)
{int b,c ;
……,
}
char c1,c2 ;
char f2(int x,int y)
{int i,j ;
………,
}
main()
{ int m,n ;
………
}
全局
变量
p,q
的有
效范
围
全局
变量
c1、
c2
的有
效范
围
变量 p,q,c1,c2都是全
局变量,但是它们的作用范
围是不同的。
全局变量也称为 外部变量,
通常定义中全局变量的变量
名的第一个字母大写便于识
别。
float Max,Min;
float average(float array[],int n)
{ int i; float 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+=array[i];
}
return(sum/n);
}
main()
{ int i; float ave,score[10];
/*Input */
ave=average(score,10);
printf("max=%6.2f\nmin=%6.2f\n
average=%6.2f\n",Max,Min,ave);
}
作
用
域
Max
Min
例如:求 10个学生
成绩的平均分,
最高分、最低分
十个学生的成绩放
在数组 score 中
最高分和最低分
分别用全局变量
Max 和 Min来表示。
int a=3,b=5;
max(int a,int b)
{ int c;
c=a>b?a:b;
return(c);
}
main()
{ int a=8;
printf("max=%d",max(a,b));
}
例,外部变量与局部变量同名
运行结果,max=8
如果在同一
个源文件中
,外部变量
与局部变量
同名,则在
局部变量的
作用范围内
,外部变量
被“屏蔽”
,即不起作
用。
§ 8.9 变量的存储类型
一,动态存储方式与静态存储方式
在前面我们看到,每一个变量从它的 作用域 角度来分,可以分为
全局变量 和 局部变量 。
下面我们考虑变量在内存中的存储情况,C语言把用户使用的内
存分为三部分(如图):即
1、程序区
2、静态存储区
3、动态存储区
程序区
静态存储区
动态存储区
用
户
区
内存
变量
的存
储区
在静态区中存的变量是在 源程序编译 时分配存储单元的,在 程序
执行完毕 才释放。
动态区中的变量是在程序 执行过程 中分配存储单元的,并在 程序
执行过程中 释放。
那么 什么类型的变量放在 静态存储区?
什么类型的变量放在 动态存储区 呢?
二、变量的存储类别
我们现在从两类变量( 全局变量, 局部变量 )出发来考虑。
首先 在 C语言中规定所有的 全局变量 都放在 静态存储区 中。
对局部变量分以下几种情况来处理,
局部变量,
1,auto 变量 (自动变量)
2,static 变量 (静态局部变量)
3,register 变量 (寄存器变量)
1,auto 变量 (自动变量 )
所有 未声明为 static存储类别 的函数中的 局部变量,就是 auto变量。
auto类别变量是动态的分配存储空间,数据存储在 动态存储区 中。
自动变量使用关键字 auto( 可以省略)作存储类别声明。例如,
int f(int a) int f(int a)
{ auto int b,c=3; {int b,c=3;
……… ………
} }
函数中形参和在函数中定义的变量都是 auto类型变量,这
类变量是在调用该函数时系统会给它们分配存储空间,在
函数调用结束时就释放这些存储空间。因此这类局部变量
就称为 自动变量 。
2、用 static声明局部变量 (静态局部变量)
用 static声明的局部变量称为 静态局部变量,特点是变量存储在 静
态存储区 中,函数编译时赋初值,函数调用结束后不释放存储单元
,其值作为下次调用的初始值。静态局部变量用关键字 static声明。
例如:考察静态局部变量的值。
f(int a) main()
{auto int b=0; {int a=2,i ;
static int c=3; for(i=0;i<3;i++)
b=b+1; printf(“%d”,f(a));
c=c+1; }
return(a+b+c);
} 运行结果,7 8 9
3,register 变量(寄存器变量)
凡是声明为 register类别的变量,不是保存在内存中,而是保存
在 CPU中的寄存器中。声明方式如下,
int fac( int n) main()
{register int i,f=1; {int i;
for(i=1;i<=n;i++) for(i=1; i<=5 ;i++)
f=f*i; printf(“%d!=%d#n”,i,fac(i));
return(f); }
}
三、关于对外部变量声明
1、在一个文件内声明外部变量
外部变量(即全局变量)是在函数的外部定义的
,它的作用域为从变量的定义处开始到文件结束。如
果在定义点之前的函数想引用该外部变量,则应该在
引用之前用关键字 extern对该变量作, 外部变量声明
” 。表示该变量是一个已经定义的外部变量。有了此
声明,就可以从, 声明, 处起,合法的使用该变量。
例如,
int max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
main()
{ extern int a,b;
printf("max=%d",max(a,b));
}
int a=13,b=-8;
extern a,b;
int max()
{ int z;
z=a>b?a:b;
return(z);
}
main()
{
printf("max=%d",max());
}
int a=13,b=-8;
LT30
2、在多个文件的程序中声明外部变量
int A;
extern float x;
main()
{ int n ;
,
,
,
}
extern A;
func2()
{,
,
,
}
file1.c
file2.c
3、用 static声明外部变量
目的是限制外部变量在其它文件中使用,只能在所定义的文件
中使用。例如,
file1.c file2.c
static int A; extern int A;
main() fun(int n)
{ {
…… ……,,
} A=A*n;
……
}
A,变量的存储方式小节
程序区
静态存储区
动态存储区
全局变量、局部静态变量
形参变量
局部动态变量( auto register)
函数调用现场保护和返回地址等
全局变量(定义在函数外部),
有效范围是 从定义处开始到文件结束。
局部变量(定义在函数内部),
有效范围是 在所定义的函数内。
B,变量的作用范围小节
C,变量的声明方式小节
[auto] 类型符 变量名 是自动变量(局部变量)
static 类型符 变量名
register 类型符 变量名 声明为寄存器局部变量
extern 变量名 声明外部变量,扩展作用域
声明静态的局部变量
声明静态的外部变量
D,变量存在的时间小节
全局变量(外部变量),在程序的整个运行期间。
静态局部变量(用 static声明的局部变量 ),
在程序的整个运行期间。
自动变量(未用 static声明的局部变量 ),
从函数调用到 该函数 结束。
寄存器变量,属于自动变量。
作业,1、阅读 8.10与 8.11节
2,186页 8.3,8.4,8.9 题
?变量存储类型
静态 动态 存储方式
程序整个运行期间 函数调用开始至结束 生存期
编译时赋初值,只赋一次 每次函数调用时 赋初值
自动赋初值 0或空字符 不确定 未赋初值
静态存储区 动态区 存储区 寄存器
局部变量 外部变量
作用域 定义变量的函数或复合语句内 本文件 其它文件
?局部变量默认为 auto型
?register型变量个数受限,且不能为 long,double,float型
?局部 static变量具有 全局寿命 和 局部可见性
?局部 static变量具有 可继承性
?extern不是变量定义,可扩展外部 变量作用域
register 局部 static auto 外部 static 外部 存储类别
例 文件 file1.c
int a;
main( )
{ ……,
……,
f2;
……,
f1;
……,
}
f1( )
{ auto int b;
………
f2;
…….,
}
f2( )
{ static int c;
………
}
C作用域
b作用域
a作用域
main f2 f1 main f1 f2 main
a生存期,
b生存期,
c生存期,
例 auto 变量的作用域
main()
{ int x=1;
void prt(void);
{ int x=3;
prt();
printf(“2nd x=%d\n”,x);
}
printf(“1st x=%d\n”,x);
}
void prt(void)
{ int x=5;
printf(“3th x=%d\n”,x);
}
运行结果,
3th x=5
2nd x=3
1st x=1
x=1作用域
x=1作用域
x=3作用域
x=5作用域
main()
{ void increment(void);
increment();
increment();
increment();
}
void increment(void)
{ int x=0;
x++;
printf(“%d\n”,x);
}
例 局部静态变量值具有可继承性
运行结果,1
1
1
main()
{ void increment(void);
increment();
increment();
increment();
}
void increment(void)
{ static int x=0;
x++;
printf(“%d\n”,x);
}
运行结果,1
2
3
例 变量的寿命与可见性
#include <stdio.h>
int i=1;
main()
{ static int a;
register int b=-10;
int c=0;
printf("-----MAIN------\n");
printf("i:%d a:%d \
b:%d c:%d\n",i,a,b,c);
c=c+8;
other();
printf("-----MAIN------\n");
printf("i:%d a:%d \
b:%d c:%d\n",i,a,b,c);
i=i+10;
other();
}
other()
{ static int a=2;
static int b;
int c=10;
a=a+2; i=i+32; c=c+5;
printf("-----OTHER------\n");
printf("i:%d a:%d \
b:%d c:%d\n",i,a,b,c);
b=a;
}
-------Main------
i:1 a:0 b:-10 c:0
------Other------
i:33 a:4 b:0 c:15
-------Main-----
i:33 a:0 b:-10 c:8
-------Other-------
i:75 a:6 b:4 c:15
全局 i 1
main,a 0
b:-10 register
main:c 0
静态
存储区
动态
存储区
other,a 2
other,b 0
other,c 10
8
4
33
15
4
43
6
75
6
main()
{ void gx(),gy();
extern int x,y;
printf(“1,x=%d\ty=%d\n”,x,y);
y=246;
gx();
gy();
}
void gx()
{ extern int x,y;
x=135;
printf(“2,x=%d\ty=%d\n”,x,y);
}
int x,y;
void gy()
{ printf(“3,x=%d\ty=%d\n”,x,y);
}
例 用 extern扩展外部变量作用域
运行结果,
1,x=0 y=0
2,x=135 y=246
3,x=135 y=246
例 引用其它文件中的变量,输出 a?b和 a的 m次方
int a;
main()
{ int power(int n);
int b=3,c,d,m;
printf("Enter the number a and its power:\n");
scanf("%d,%d",&a,&m);
c=a*b;
printf("%d*%d=%d\n",a,b,c);
d=power(m);
printf("%d**%d=%d",a,m,d);
}
extern int a;
int power(int n)
{ int i,y=1;
for(i=1;i<=n;i++)
y*=a;
return(y);
}
源程序文件1
预编译命令
说明部分 执行部分
函数1 函数n
源程序文件i 源程序文件n
C程序
C程序结构
?C是 函数式 语言
?必须有且只能有一个名为 main的主函数
?C程序的执行总是 从 main函数开始,在 main中结束
?程序中函数 都是平行的,不能嵌套定义,可以 嵌套 调用
第八章 函数
main()
{ int a,b;
int c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf("Max is %d\n",c);
}
max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例如下面是一个 C源程序,它由两个函数组成,
即 main 与 max 函数
main()
{ int a,b;
int 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);
}
函数分类
1、标准函数(库函数:由系统提供 ),
例如,printf 函数, sqrt函数,
2、用户自定义函数, 例如,上面程序中的
max 函数
max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
函数的定义
?一般格式
函数体
函数类型 函数名 ( 形参类型说明表 )
{
说明部分
语句部分
}
例 有参函数(现代风格)
int max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例 有参函数(现代风格)
int max(int x,y)
{ int z;
z=x>y?x:y;
return(z);
}
例 空函数
dummy( )
{ }
函数体为空
例 无参函数
printstar( )
{ printf(“**********\n”); }
省略时为 int 类型
void 类型是无返回
值
形参
§ 8.3 函数的返回值
返回语句形式,
return(表达式 );
或 return 表达式 ;
或 [return;]
功能,使程序控制从被调用
函数返回到调用函数中,同时把返值带给调用
函数
函数结束标志,遇到 return 语句 或执行到
最后一个 }
main()
{ int c,a=1,b=2;
c=add(a,b);
printf(,c= %d,,c);
}
int add(int x,int y)
{ int z;
z=x+y ;
return(z);
} 演示
printstar()
{ printf("**********");
}
main()
{ int a;
a=printstar();
printf("%d",a);
}
例 函数带回不确定值
输出,10
void printstar()
{ printf("**********");
}
main()
{ int a;
a=printstar();
printf("%d",a);
}
编译错误!
演示
例 函数返回值类型转换
main()
{ 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=x>y?x:y;
return(z);
}
运行情况,
输入,2.5,3.5
调用函数
c=max(2.5,3.5)
返回值( return(3.5))
输出,Max is 3
演示
§ 8.4 函数的调用
调用形式
函数名 (实参 1,实参 2,…,实参 k);
对照函数定义形式
类型 函数名 (类型 1 形参 1,类型 2 形参 2,….)
{……}
说明,实参与形参 个数相等,
类型一致, 按顺序一一对应
调用方式
函数语句,
例 printstar();
printf(“Hello,World!\n”);
函数表达式,
例 m=max(a,b)*2;
函数参数,
例 printf(“%d”,max(a,b));
m=max(a,max(b,c));
main()
{ int c,a=1,b=2;
c=add(a,b)*2;
printf(,c= %d,,c);
}
int add(int x,int y)
{ int z;
z=x+y ;
return(z);
}
printstar()
{ printf("**********");
}
main()
{ printstar();
}
函数声明
1、什么是函数声明?
函数声明的一般形式,
类型 函数名 (形参类型 [形参名 ],….,) ;
例如,int max( int x,in y) ;
或 float add(float,float ) ;
2、函数声明的作用是什么?
告诉编译系统 函数类型、参数个数及类型,以便检
验
3、函数声明放在什么位置?
放在定义变量的位置,即 程序的数据说明部分
4、什么情况可以省略函数声明?
1) 若函数返值是 char或 int型, 系统自动按 int型处
理
2) 被调用函数定义出现在主调函数之前
3) 在所有函数定义之前,在函数外部做了函数声
明。
例 函数声明举例
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);
}
int型函数可不作函数说明
/*ch7_5.c*/
float add(float x,float y)
{ float z;
z=x+y;
return(z);
}
main()
{ float a,b,c;
scanf("%f,%f",&a,&b);
c=add(a,b);
printf("sum is %f",c);
}
被调函数出现在主调函数
之前,不必函数说明
/*ch8_5.c*/
main()
{ float add(float,float); /*function declaration*/
float a,b,c;
scanf("%f,%f",&a,&b);
c=add(a,b);
printf("sum is %f",c);
}
float add(float x,float y)
{ float z;
z=x+y;
return(z);
}
float add();
#i clude <stdio.h>
foat m x(float x,float y);
main()
{ float a,b;
int c;
scanf("%f,%f",&a,&b);
c=max(a,b);
printf("Max is %d\n",c);
}
foat max(float x,float y)
{ float z;
z=x>y?x:y;
return(z);
}
在函数外面做了
声明,
所有调用函数
不必再声明
§ 8.5 函数参数及其传递方式
?形参与实参
?形式参数:定义函数时函数名后面括号中的变量名
?实际参数:调用函数时函数名后面括号中的表达式
c=max(a,b); ( main 函数)
( max 函数) max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例 比较两个数并输出大者 main()
{ int a,b,c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf("Max is %d",c);
}
max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
形参
实参
?说明,
?实参必须有确定的值
?形参必须指定类型
?形参与实参 类型一致,个数相同
?形参在函数被调用前不占内存 ;函数调用
时为形参分配内存;调用结束,内存释放
§ 8.5 函数参数及其传递方式
形参与实参
例 计算 x的立方
#include <stdio.h>
float c(float x)
{ return(x*x*x);
}
main()
{ float a,p;
printf("Please input value of a:");
scanf("%f",&a);
p=c(a);
printf(”Cube of %.4f is %.4f\n",a,p);
}
x
a
p
××
××
1.2
1.2
1.728
内存的变化情况
运行情况,
Please input value of a,1.2
?参数传递方式
?值传递 方式
?方式:函数调用时,为形参分配单元,并将实参
的值 复制 到形参中;调用结束,形参单元被释
放,实参单元仍保留并维持原值
?特点,
?形参与实参占用 不同 的内存单元
?单向 传递
?实参可以是常量、变量或表达式,但必须
有确定的值。
7 11 x,y,调用前,
调用结束,7 11 x,y,
例 交换两个数
#include <stdio.h>
main()
{ int x=7,y=11;
printf("x=%d,\ty=%d\n",x,y);
printf("swapped:\n");
swap(x,y);
printf("x=%d,\ty=%d\n",x,y);
}
swap(int a,int b)
{ int temp;
temp=a; a=b; b=temp;
}
调用,
7 11 a,b,
7 11 x,y,
swap,7 11 x,y,
11 7 a,b,
temp
运行,
x=7 y=11
swapped,x=7 y=11
#include <stdio.h>
long sum(int a,int b);
long factorial(int n);
main()
{ int n1,n2;
long a;
scanf("%d,%d",&n1,&n2);
a=sum(n1,n2);
printf("a=%1d",a);
}
long sum(int a,int b)
{
long c1,c2;
c1=factorial(a);
c2=factorial(b);
return(c1+c2);
}
long factorial(int n)
{ long rtn=1;
int i;
for(i=1;i<=n;i++)
rtn*=i;
return(rtn);
}
long sum(int a,int b);
long factorial(int n);
文件包含编译预处理命令
函数类型说明
函数定义
函数调用
函数调用
函数返回值
形参
实参
§ 8.6 函数的嵌套与递归调用
?嵌套调用
C规定,函数定义不可嵌套,但 可以嵌套调用 函数
main( )
调用函数 a
结束
a函数 b函数
调用函数 b
? ? ? ?
?
? ? ?
?
函数嵌套
main()
{ float add( float,float ) ;
int a=2,b=3,c ;
c=add(a,b);
printf(,c= %d,,c);
float add(float x,float y)
{ float z ;
z=x+y ;
return(z) ;
}
}
嵌套调用
例 求三个数中最大数和最小数的差值
#include <stdio.h>
int dif(int x,int y,int z);
int max(int x,int y,int z);
int min(int x,int y,int z);
void main()
{ int a,b,c,d;
scanf("%d%d%d",&a,&b,&c);
d=dif(a,b,c);
printf("Max-Min=%d\n",d);
}
int dif(int x,int y,int z)
{ return max(x,y,z)-min(x,y,z); }
int max(int x,int y,int z)
{ int r;
r=x>y?x:y;
return(r>z?r:z);
}
int min(int x,int y,int z)
{ int r;
r=x<y?x:y;
return(r<z?r:z);
} main( )
调用函数 dif
输出
结束
dif函数 max函数
调用函数 max
调用函数 min min函数
作业,186页 8.1, 8.3
下次实验:实验十五( 写实验报告 )
实验项目,函数的定义和调用、变量
的存储属性
实验目的和实验内容按实验十五写
实验报告中除实验十五外,自己任选
有关函数题目写上。
关于字符型数组结尾中标识 ‘ \0? 问题
在下面情况,系统会自动在数组结尾加‘ \0?
1,char ch[6]=“abcde” 其中 ch[5]=?\0?
2,char ch[10]; 如果输入 5个字符,则第六元
scanf(“%s”,ch); 素 ch[5]=?\0 ? 而 ch[6]—ch[9]
的值不确定
或 char ch[10];
gets(ch);
注意 与 ch[10]=“abcde” ; 这时 ch[5]--ch[9] 都是‘ \0? 的区别
3,char ch[5]={?a?,?b?,?c?}; ch[3 ]=?\0?, ch[4 ]=?\0?
141页 7.8题
找出一个二维数组中的鞍点,即该位置上的元素在该行
上最大,在该列上最小。也可能没有鞍点。例如 下面的
3行 4列数组,
1 2 3 4
4
3
5 3 6
7 6 5
位于第 0行
,第 3列的
4就是一个
鞍点。
2 4 90 7
3
9
4 5 8
3 2 1
而二维数
组中无鞍
点。
定义矩阵的行 N,列 M
for(i=0;i<N;i++)
for(j=0;j<M;j++)
输入矩阵元素
for(i=0;i<N;i++)
max=a[i][0]
for(j=0;j<M;j++)
a[i][j]>max
T F
max=a[i][j],maxj=j
for(k=0,flag1=1; k<N && flag1 ; k++)
max>a[k][maxj]
T F
flag1=0
flag1=1
T F
输出鞍点的行、列号及其值
flag2=0
T F
输出“矩阵中无鞍点”
flag2=0
flag2=1
c程序,
#define N 3
#define M 4
main()
{ int i,j,k,flag1,flag2,a[N][M],max,maxi,maxj;
for(i=0;i<N;i++)
{printf(“第 %d行?\n”,i);
for(j=0;j<M;j++)
scanf(“%d”,&a[i][j]);
}
for(i=0; i<N; i++)
{for(j=0;j<M;j++)
printf(“%d”,a[i][j]);
printf(“\n”);
}
flag2=0;
for( i=0; i<N; i++)
{max=a[i][0];
for(j=0;j<M;j++)
if(a[i][j]>max) { max=a[i][j] ; maxj=j ;}
for(k=0,flag1=1; k<N && flag1 ; k++)
if(max>a[k][maxj])
flag1=0;
if(flag1)
{printf(“\n 第 %d行,第 %d列的 %d是鞍点 \n”,i,maxj,max);
flag2=1;
}
if(!flag2)
printf(“\n 数组中无鞍点 ! \n,);
}
}
本次课内容,
一、函数的递归调用
二、数组作为函数的参数
三、局部变量和全局变量
四、变量的存储类型
*五、内部函数和外部函数
*六、如何运行一个多文件的程序
自学
首先把上次课的内容复习一下
1、函数的定义形式
类型标识符 函数名 ([形式参数表 ])
{声明部分
语句
}
例如,float max(float x,float y)
{float z;
z=x>y?x:y;
return(t);
}
2、函数的调用
函数名 (实参表 ) ; 例如在主调函数中 c=max(a,b);
函数首部
结尾没有分号
3、形参与实参
定义函数中的参数为形参,形参一定是变量。
调用函数使用的参数为实参,实参可以是变量、
常量或表达式。
4、函数通过 return 语句返回值。
5、在主调函数中对被调函数的声明方式,
函数定义部分 + ;
例如,
main()
{float max(int x,int y) ;
……,
}
函数声明
main()
{
??
d=f1(n,m);
??
}
int f1(int a,int b)
{
??
c=f2(a,b);
??
}
int f2(int x,int y)
{
??
}
1
2
3
在一个源程序文件中所有函数的定义都是并列的,例如,
6、函数的位置
函数不能嵌套定义,也不能交叉定义。
例如下面的嵌套定义是错误的
main()
{
???
int f(int a,int b)
{
???
}
}
下面的交叉定义也是错误的
main()
{ …….,
int f(int a,int b)
{ ………
………,
}
}
主调
函数
被调
函数
主调
函数
被调
函数
7、函数的嵌套调用
例如,
main()
{ ……
s=f1(a,b);
………
}
f1(int n,int m)
{ …….,
y=f2(c);
……,
}
f2(int x )
{
……
}
8.6 函数的递归调用
定义:函数直接或间接的调用自身叫函数的递归调用
f( )
调 f 调 f2 调 f1
f1( ) f2( )
int f(int x)
{ int y,z;
……
z=f(y);
……,
return(2*z);
}
int f1(int x)
{ int y,z;
……
z=f2(y);
……,
return(2*z);
}
int f2(int t)
{ int a,c;
……
c=f1(a);
……,
return(3+c);
}
一个问题要采用递归方法来解决时
必须符合以下三个条件,
1、可以把一个问题转化为一个新问题,而这个新问
题的解决方法仍与原问题的解法相同 ;只是所处
理的对象有所不同,但他们只是有规律的递增或
递减,
3、必须要有一个明确的结束递归的条件,否则
递归将无止境地进行下去,也就是说必须要有
某个终止递归的条件,
2、可以通过转化过程使问题得到解决,
例 求 n的阶乘 #include <stdio.h> int fac(int n)
{ int f;
if(n<0) printf("n<0,data error!");
else if(n= =0 ) f=1;
else f = fac(n-1)*n;
return(f);
}
main()
{ int n,y;
printf("Input a integer number:");
scanf("%d",&n);
y=fac(n);
printf("%d! =%5d",n,y);
}
设 f(n)=n× (n-1)× … × 2× 1
则有,
? ?
?
> - ×
= =
) 0 ( )! 1 (
) 0 ( 1 !
n n n
n n
f(n)=
1 (n=0)
n × f(n-1) (n>0)
上面递归程序的执行过程如下:设 n=4
…,
y=fac(4)
….,
主函数
…
y=fac(3)*4
fac=24
….,
…
y=fac(2)*3
fac=6
……
…
y=fac(1)*2
fac=2
……
…
y=fac(0)*1
fac=1
……
n,4 n,3 n,2 n,1
fac=24 fac=6 fac=2 fac=1
…
y=1
fac=1
…
n,0
fac=1
本次调用结束时
返回值
形参
§ 8.7 数组作为函数的参数
数组作为函数参数分两种情况:即 数组元素 作函数参数和 数
组名 作函数的参数。
数组元素作函数参数时和一般变量作函数参数是一样的,是值传递 。
数组名(是数组存储区的首地址)作函数参数时与数组元素作参
数不同,是地址传递 。分别介绍如下,
1、数组元素作函数实参
传递方式是单向的,值传递。例如,
main() int sum(int x)
{int sum(int x); {int y;
int i,s=0,a[5]={1,2,3,4,5}; y=x*10;
for (i=0;i<10;i++) return(y);
s=s+sum(a[i]); }
printf(“s=%d\n”,s); 输出结果,( s=10+20+30+40+50 )
} s=150
2,数组名作函数参数
这时实参是数组名,因为数组名是数组存储区的首地址,
所以把数组名传递给形参的过程,就是把数组存储区的
首地址传给形参。这种传递方式称为,地址传递
要求:实参和形参分别定义数组,且类型应一致,形参
数组大小 (多维数组第一维 )可不指定。
形参数组名实际是 地址变量。 (因为用来存放数组存储
区首地址)
这时实参和形参对应同一个数组存储区,所以传递是双
向的。通过以下的例子来进一步理解。
例 1,求学生的平均成绩
#include <stdio.h>
float average(int stu[10],int n);
main()
{ int score[10],i;
float av;
printf("Input 10 scores,\n");
for( i=0; i<10; i++ )
scanf("%d",&score[i]);
av=average(score,10);
printf("Average is,%.2f",av);
}
float average(int stu[10],int n)
{ int i;
float av,total=0;
for( i=0; i<n; i++ )
total += stu[i];
av = total/n;
return av;
}
实参用数组名
形参用数组定义,?int stu[ ]
,
,
2
1
0
9
score
56
23
12
…,
…,
88
stu
例 2,数组排序 ---选择排序
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[i];
array[i]=array[k];
array[k]=t;
}
}
}
main()
{ int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
0
1
2
3
4
5
6
7
8
9
a 49
68
57
32
9
99
27
13
76
88
array
首地址
2000
0
1
2
3
4
5
6
7
8
9
a 9
13
27
32
49
57
68
76
88
99
array
首地址
2000
例 3,求二维数组中最大元素值
a00 a01 a02 a03
a10 a10 a12 a13
a20 a21 a22 a23
int max_value(int array[3][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);
}
main()
{ 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));
}
多维形参数组第一维维数
可省略,第二维必须相同
? int array[][4]
1 3 5 7
2 4 6 8
15 17 34 12
§ 8.8 局部变量和全局变量
一、什么是 局部变量?
在一个函数内部定义的变量称为局部变量。 局部变
量的有效范围是所在定义的函数内,也就是说只有在
本函数内才能使用他们 。在此函数以外是不能使用这
些变量的。例如:在下面源程序中
float f1(int a)
{int b,c;
………
}
char f2(int x,int y)
{ int b,c ;
……
}
a,b,c的有
效范围
x,y,b,c的
有效范围
main()
{int m,n;
………
}
m,n的有
效范围
说明,
1、主函数中定义的变量也属于局部变量,例如上面的变量 m,n 。
也只能在主函数内使用。
2、不同函数中可以使用相同名字的变量,在内存中占有不同的存
储单元。例如上面的函数 f1 和 f2中均使用局部变量 b 和 c 。
3,形参也是局部变量。
4、在一个函数内部,也可以在复合语句中定义变量,这些变量只
能在本复合语句中有效,这种复合语句称为“分程序”或“程序
块”。例如,
main()
{ int a,b ;
………….,
{ int c;
c=a+b;
……,
}
…….,
}
C的有效范
围
a,b的有效
范围
例 不同函数中同名变量
main()
{ int a,b;
a=3;
b=4;
printf("main:a=%d,b=%d\n",a,b);
sub();
printf("main:a=%d,b=%d\n",a,b);
}
sub()
{ int a,b;
a=6;
b=7;
printf("sub:a=%d,b=%d\n",a,b);
}
例 不同函数中同名变量
main()
{ int a,b;
a=3;
b=4;
printf("main:a=%d,b=%d\n",a,b);
sub();
printf("main:a=%d,b=%d\n",a,b);
}
sub()
{ int a,b;
a=6;
b=7;
printf("sub:a=%d,b=%d\n",a,b);
}
运行结果,
main:a=3,b=4
sub:a=6,b=7
main:a=3,b=4
地址
地址
3 4
6 7
函数 main中的变量 a,b的存储
单元
2000 2050
a b
b a
3100 3500
函数 sub中的变量 a,b的存储
单元
地址
地址
二、什么是 全局变量?
定义在函数外部的变量称为
全局变量,全局变量可以为本
文件中其它函数所使用。它的
有效范围为从定义变量的位置
开始到本源文件结束。例如,
int p=1,q=5;
float f1(int a)
{int b,c ;
……,
}
char c1,c2 ;
char f2(int x,int y)
{int i,j ;
………,
}
main()
{ int m,n ;
………
}
全局
变量
p,q
的有
效范
围
全局
变量
c1、
c2
的有
效范
围
变量 p,q,c1,c2都是全
局变量,但是它们的作用范
围是不同的。
全局变量也称为 外部变量,
通常定义中全局变量的变量
名的第一个字母大写便于识
别。
float Max,Min;
float average(float array[],int n)
{ int i; float 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+=array[i];
}
return(sum/n);
}
main()
{ int i; float ave,score[10];
/*Input */
ave=average(score,10);
printf("max=%6.2f\nmin=%6.2f\n
average=%6.2f\n",Max,Min,ave);
}
作
用
域
Max
Min
例如:求 10个学生
成绩的平均分,
最高分、最低分
十个学生的成绩放
在数组 score 中
最高分和最低分
分别用全局变量
Max 和 Min来表示。
int a=3,b=5;
max(int a,int b)
{ int c;
c=a>b?a:b;
return(c);
}
main()
{ int a=8;
printf("max=%d",max(a,b));
}
例,外部变量与局部变量同名
运行结果,max=8
如果在同一
个源文件中
,外部变量
与局部变量
同名,则在
局部变量的
作用范围内
,外部变量
被“屏蔽”
,即不起作
用。
§ 8.9 变量的存储类型
一,动态存储方式与静态存储方式
在前面我们看到,每一个变量从它的 作用域 角度来分,可以分为
全局变量 和 局部变量 。
下面我们考虑变量在内存中的存储情况,C语言把用户使用的内
存分为三部分(如图):即
1、程序区
2、静态存储区
3、动态存储区
程序区
静态存储区
动态存储区
用
户
区
内存
变量
的存
储区
在静态区中存的变量是在 源程序编译 时分配存储单元的,在 程序
执行完毕 才释放。
动态区中的变量是在程序 执行过程 中分配存储单元的,并在 程序
执行过程中 释放。
那么 什么类型的变量放在 静态存储区?
什么类型的变量放在 动态存储区 呢?
二、变量的存储类别
我们现在从两类变量( 全局变量, 局部变量 )出发来考虑。
首先 在 C语言中规定所有的 全局变量 都放在 静态存储区 中。
对局部变量分以下几种情况来处理,
局部变量,
1,auto 变量 (自动变量)
2,static 变量 (静态局部变量)
3,register 变量 (寄存器变量)
1,auto 变量 (自动变量 )
所有 未声明为 static存储类别 的函数中的 局部变量,就是 auto变量。
auto类别变量是动态的分配存储空间,数据存储在 动态存储区 中。
自动变量使用关键字 auto( 可以省略)作存储类别声明。例如,
int f(int a) int f(int a)
{ auto int b,c=3; {int b,c=3;
……… ………
} }
函数中形参和在函数中定义的变量都是 auto类型变量,这
类变量是在调用该函数时系统会给它们分配存储空间,在
函数调用结束时就释放这些存储空间。因此这类局部变量
就称为 自动变量 。
2、用 static声明局部变量 (静态局部变量)
用 static声明的局部变量称为 静态局部变量,特点是变量存储在 静
态存储区 中,函数编译时赋初值,函数调用结束后不释放存储单元
,其值作为下次调用的初始值。静态局部变量用关键字 static声明。
例如:考察静态局部变量的值。
f(int a) main()
{auto int b=0; {int a=2,i ;
static int c=3; for(i=0;i<3;i++)
b=b+1; printf(“%d”,f(a));
c=c+1; }
return(a+b+c);
} 运行结果,7 8 9
3,register 变量(寄存器变量)
凡是声明为 register类别的变量,不是保存在内存中,而是保存
在 CPU中的寄存器中。声明方式如下,
int fac( int n) main()
{register int i,f=1; {int i;
for(i=1;i<=n;i++) for(i=1; i<=5 ;i++)
f=f*i; printf(“%d!=%d#n”,i,fac(i));
return(f); }
}
三、关于对外部变量声明
1、在一个文件内声明外部变量
外部变量(即全局变量)是在函数的外部定义的
,它的作用域为从变量的定义处开始到文件结束。如
果在定义点之前的函数想引用该外部变量,则应该在
引用之前用关键字 extern对该变量作, 外部变量声明
” 。表示该变量是一个已经定义的外部变量。有了此
声明,就可以从, 声明, 处起,合法的使用该变量。
例如,
int max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
main()
{ extern int a,b;
printf("max=%d",max(a,b));
}
int a=13,b=-8;
extern a,b;
int max()
{ int z;
z=a>b?a:b;
return(z);
}
main()
{
printf("max=%d",max());
}
int a=13,b=-8;
LT30
2、在多个文件的程序中声明外部变量
int A;
extern float x;
main()
{ int n ;
,
,
,
}
extern A;
func2()
{,
,
,
}
file1.c
file2.c
3、用 static声明外部变量
目的是限制外部变量在其它文件中使用,只能在所定义的文件
中使用。例如,
file1.c file2.c
static int A; extern int A;
main() fun(int n)
{ {
…… ……,,
} A=A*n;
……
}
A,变量的存储方式小节
程序区
静态存储区
动态存储区
全局变量、局部静态变量
形参变量
局部动态变量( auto register)
函数调用现场保护和返回地址等
全局变量(定义在函数外部),
有效范围是 从定义处开始到文件结束。
局部变量(定义在函数内部),
有效范围是 在所定义的函数内。
B,变量的作用范围小节
C,变量的声明方式小节
[auto] 类型符 变量名 是自动变量(局部变量)
static 类型符 变量名
register 类型符 变量名 声明为寄存器局部变量
extern 变量名 声明外部变量,扩展作用域
声明静态的局部变量
声明静态的外部变量
D,变量存在的时间小节
全局变量(外部变量),在程序的整个运行期间。
静态局部变量(用 static声明的局部变量 ),
在程序的整个运行期间。
自动变量(未用 static声明的局部变量 ),
从函数调用到 该函数 结束。
寄存器变量,属于自动变量。
作业,1、阅读 8.10与 8.11节
2,186页 8.3,8.4,8.9 题
?变量存储类型
静态 动态 存储方式
程序整个运行期间 函数调用开始至结束 生存期
编译时赋初值,只赋一次 每次函数调用时 赋初值
自动赋初值 0或空字符 不确定 未赋初值
静态存储区 动态区 存储区 寄存器
局部变量 外部变量
作用域 定义变量的函数或复合语句内 本文件 其它文件
?局部变量默认为 auto型
?register型变量个数受限,且不能为 long,double,float型
?局部 static变量具有 全局寿命 和 局部可见性
?局部 static变量具有 可继承性
?extern不是变量定义,可扩展外部 变量作用域
register 局部 static auto 外部 static 外部 存储类别
例 文件 file1.c
int a;
main( )
{ ……,
……,
f2;
……,
f1;
……,
}
f1( )
{ auto int b;
………
f2;
…….,
}
f2( )
{ static int c;
………
}
C作用域
b作用域
a作用域
main f2 f1 main f1 f2 main
a生存期,
b生存期,
c生存期,
例 auto 变量的作用域
main()
{ int x=1;
void prt(void);
{ int x=3;
prt();
printf(“2nd x=%d\n”,x);
}
printf(“1st x=%d\n”,x);
}
void prt(void)
{ int x=5;
printf(“3th x=%d\n”,x);
}
运行结果,
3th x=5
2nd x=3
1st x=1
x=1作用域
x=1作用域
x=3作用域
x=5作用域
main()
{ void increment(void);
increment();
increment();
increment();
}
void increment(void)
{ int x=0;
x++;
printf(“%d\n”,x);
}
例 局部静态变量值具有可继承性
运行结果,1
1
1
main()
{ void increment(void);
increment();
increment();
increment();
}
void increment(void)
{ static int x=0;
x++;
printf(“%d\n”,x);
}
运行结果,1
2
3
例 变量的寿命与可见性
#include <stdio.h>
int i=1;
main()
{ static int a;
register int b=-10;
int c=0;
printf("-----MAIN------\n");
printf("i:%d a:%d \
b:%d c:%d\n",i,a,b,c);
c=c+8;
other();
printf("-----MAIN------\n");
printf("i:%d a:%d \
b:%d c:%d\n",i,a,b,c);
i=i+10;
other();
}
other()
{ static int a=2;
static int b;
int c=10;
a=a+2; i=i+32; c=c+5;
printf("-----OTHER------\n");
printf("i:%d a:%d \
b:%d c:%d\n",i,a,b,c);
b=a;
}
-------Main------
i:1 a:0 b:-10 c:0
------Other------
i:33 a:4 b:0 c:15
-------Main-----
i:33 a:0 b:-10 c:8
-------Other-------
i:75 a:6 b:4 c:15
全局 i 1
main,a 0
b:-10 register
main:c 0
静态
存储区
动态
存储区
other,a 2
other,b 0
other,c 10
8
4
33
15
4
43
6
75
6
main()
{ void gx(),gy();
extern int x,y;
printf(“1,x=%d\ty=%d\n”,x,y);
y=246;
gx();
gy();
}
void gx()
{ extern int x,y;
x=135;
printf(“2,x=%d\ty=%d\n”,x,y);
}
int x,y;
void gy()
{ printf(“3,x=%d\ty=%d\n”,x,y);
}
例 用 extern扩展外部变量作用域
运行结果,
1,x=0 y=0
2,x=135 y=246
3,x=135 y=246
例 引用其它文件中的变量,输出 a?b和 a的 m次方
int a;
main()
{ int power(int n);
int b=3,c,d,m;
printf("Enter the number a and its power:\n");
scanf("%d,%d",&a,&m);
c=a*b;
printf("%d*%d=%d\n",a,b,c);
d=power(m);
printf("%d**%d=%d",a,m,d);
}
extern int a;
int power(int n)
{ int i,y=1;
for(i=1;i<=n;i++)
y*=a;
return(y);
}