第 5章 函数及变量的存储类别
5.0 概述
5.1函数的定义
5.2函数调用
5.3变量的作用域和存储类别
5.4内部函数和外部函数
5.5函数的嵌套调用和递归调用第 5章 函数及变量的存储类别
§ 5.0 概述
模块化程序设计
基本思想:将一个大的程序按功能分割成一些小模块,
特点:
各模块相对独立、功能单一、结构清晰、接口简单
控制了程序设计的复杂性
提高元件的可靠性
缩短开发周期
避免程序开发的重复劳动
易于维护和功能扩充
开发方法,自上向下,逐步分解,分而治之
C是模块化程序设计语言源程序文件1
预编译命令说明部分 执行部分函数1 函数n
源程序文件i 源程序文件n
C程序
C程序结构
C是 函数式 语言
必须有且只能有一个名为 main的主函数
C程序的执行总是 从 main函数开始,在 main中结束
函数 不能嵌套定义,可以 嵌套 调用
函数分类
从用户角度
标准函数(库函数):由系统提供
用户自定义函数
从函数形式
无参函数
有参函数使用 库函数 应注意:
1、函数功能
2、函数参数的数目和顺序,及各参数意义和类型
3、函数返回值意义和类型
4、需要使用的包含文件
#include"stdio.h"
main()
{int n,k,c;
printf("\nplease input( n,k),"); /*输入 n,k的值 */
scanf("%d,%d",&n,&k);
c=fac(n)/(fac(k)*fac(n-k)); /*三次调用函数 fac,求 n!,k!,(n-k)!*/
printf("\nC(n,k)=%d",c); /*输出计算结果 */
}
int fac(int m) /*计算一个整数的阶乘 */
{int i,s=1;
for(i=1;i<=m;i++)
s*=i;
return(s);
}
【 例 5.2】 编写程序计算组合数,n!C(n,k)= ────
k!(n-k)!
§ 5.1 函数的定义
5.1.1 一般格式 合法标识符函数返回值类型缺省 int型无返回值 void
函数体函数类型 函数名 ( 形参类型说明表 )
{
说明部分语句部分
}
现代风格,
例 有参函数(现代风格)
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”); }

printstar(void )
{ printf(“**********\n”); }
函数类型 函数名(形参表)
形参类型说明
{
说明部分语句部分
}
传统风格,
例 有参函数(传统风格)
int max(x,y)
int x,y;
{ int z;
z=x>y?x:y;
return(z);
}
int max(int x1,int x2,int x3) /*定义函数的返回值类型,函数名,形参 */
{int max;
if (x1>x2) max=x1;
else max=x2;
if (max<x3) max=x3; /*通过比较得到三个数的最大值放到 max中 */
return(max); /*返回运算结果 */
}
【 例 5.1】 编写函数,求三个整型参数的最大值。
§ 5.1.2 函数的返回值
返回语句
形式,return(表达式 );
或 return 表达式 ;
或 return;
功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数
说明:
函数中可有多个 return语句
若无 return语句,遇到函数结束的,}” 时,自动返回调用函数
若函数类型与 return语句中表达式值的类型不一致,按前者为准,
自动转换 ------函数调用转换
void型函数:无返回值的函数例 无返回值函数
void swap(int x,int y )
{ int temp;
temp=x;
x=y;
y=temp;
}
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);
}
max(float x,float y)
{ float z;
z=x>y?x:y;
return(z);
}
§ 5.2 函数的调用
5.2.1 调用形式,函数名 (实参表 )
函数语句,函数调用可单独成为一个语句例 output(); ;
printf(“Hello,World!\n”);
函数表达式,调用函数后的返回值可参加表达式的计算,这时要求被调函数带返回值。
例 sum=add(a,b);
c=fac(n)/(fac(k)*fac(n-k)) ;
函数参数,带返回值的函数调用亦可作为其它函数的参数(实际参数)
例 printf(“%d”,max(a,b));
m=max(a,max(b,c));
调用方式
函数语句,函数调用可单独成为一个语句例 output(); ;
printf(“Hello,World!\n”);
函数表达式,调用函数后的返回值可参加表达式的计算,这时要求被调函数带返回值。
例 sum=add(a,b);
c=fac(n)/(fac(k)*fac(n-k)) ;
函数参数,带返回值的函数调用亦可作为其它函数的参数(实际参数)
例 printf(“%d”,max(a,b));
m=max(a,max(b,c));
调用函数的过程是:
① 为函数的所有形参分配内存单元。
②计算各个实参表达式的值,一一对应的赋值给相应形参
(若是无参函数,上述过程不执行)。
③ 进入函数体,执行函数中的语句,实现函数的功能。
④ 执行到 return语句时,计算 return语句中表达式的值(若是无返回值函数,本项不做),返回到主调函数。
⑤ 释放形参及本函数内的局部变量所占内存,继续执行主调函数中的后继语句。
说明:
实参与形参 个数相等,类型一致,按顺序一一对应
形参与实参的结合顺序,因系统而定( Turbo C 自右向左 )
main()
{ int i=2,p;
p=f(i,++i);
printf("%d",p);
}
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=2,p;
p=f(i,i++);
printf("%d",p);
}
int f(int a,int b)
{ int c;
if(a>b) c=1;
else if(a==b) c=0;
else c=-1;
return(c);
}
运行结果,0 运行结果,1
对于函数的位置:
对于非 int函数,调用单位的位置要在被调用单位在下面,
否则编译产生错误。
解决方法是:在调用单位加上被调用函数的声 (说)明。
void f()
{

}
main( )
{

f ( );

}
void f( )
{

}
main( )
{ …
f ();

}
void f( )
{

}
main( )
{void f( );

f (); …
}
5.2.2 函数说明
对被调用函数要求:
必须是 已存在 的函数
库函数,#include <*.h>
用户自定义函数,函数类型说明
函数说明
一般形式,函数类型 函数名 (形参类型 [形参名 ],….,);
或 函数类型 函数名 ();
作用:告诉编译系统 函数类型、参数个数及类型,以便检验 ;
函数定义 与 函数说明 不同 ;
函数说明位置,程序的数据说明部分(函数内或外) ;
下列情况下,可不作函数说明
若函数返值是 char或 int型,系统自动按 int型处理 ;
被调用函数定义出现在主调函数之前 ;
有些系统 (如 Borland C++)要求函数说明指出函数返值类型和形参类型,并且对 void 和 int 型函数也要进行函数说明例 函数说明举例
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型函数可不作函数说明
( Borland C++不行)
/*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);
}
被调函数出现在主调函数之前,不必函数说明
/* */
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();
例 4.4 4.5
C语言中,函数调用是值传递方式,即函数的实际参数和形式参数之间的数据传递方向是单向的,只能由实际参数传递给形式参数,而不能由形式参数传递给实际参数,
是实际参数向形式参数单向赋值的关系 。
在内存中,形式参数与实际参数占用不同的内存单元 。
当调用函数时,给形式参数分配内存单元,将实际参数的值赋值给形式参数,调用后,形式参数单元释放,实际参数仍保留调用前的值,形式参数值的变化不影响实际参数 。
§ 5.2.3 函数参数及其传递方式
形参与实参
形式参数:定义函数时函数名后面括号中的变量名
实际参数:调用函数时函数名后面括号中的表达式
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);
}
形参实参
说明:
实参必须有确定的值
形参必须指定类型
形参与实参 类型一致,个数相同
若形参与实参类型不一致,自动按形参类型转换 ———函数调用转换
形参在函数被调用前不占内存 ;函数调用时为形参分配内存;调用结束,内存释放例 计算 x的立方
#include <stdio.h>
float cube(float x)
{ return(x*x*x);
}
main()
{ float a,product;
printf("Please input value of a:");
scanf("%f",&a);
product=cube(a);
printf(”Cube of %.4f is %.4f\n",a,product);
}
x
a
product
××
××
1.2
1.2
1.728
参数传递方式
值传递 方式
方式:函数调用时,为形参分配单元,并将实参的值 复制 到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值
特点:
形参与实参占用 不同 的内存单元
单向 传递
3 5a,b:调用前:
调用结束,3 5a,b:
例 5.6 交换两个数
#include <stdio.h>
main()
{ int a=3,b=5;
printf(“a=%d,\tb=%d\n",a,b);
printf("swapped:\n");
swap(a,b);
printf(“a=%d,\tb=%d\n",a,b);
}
swap(int x,int y)
{ int temp;
temp=x; x=y; y=temp;
}
调用
3 5x,y:
3 5a,b:
swap:
temp
3 5a,b:
x,3 5y:
如何实现,地址传递,?
只能是:函数调用时,将数据的 存储地址 作为参数传递给形参,当然此时的行参只能是能存储地址的变量(指针变量)。才能达到“双向”传送的目的。
swap(p1,p2)
int *p1,*p2;
{ int p;
p=*p1;
*p1=*p2;
*p2=p;
}
main()
{ int a,b;
scanf("%d,%d",&a,&b);
printf(“a=%d,b=%d\n”,a,b);
printf(“swapped:\n”);
swap(&a,&b);
printf(”a=%d,b=%d\n",a,b);
}
例 交换两个数
a
5 9
b调前:
a
5
9
b调 swap:
p1
&a
&b
p2
a
9
5
b交换:
p1
&a
&b
p2
a
9 5
b返回:
#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);
文件包含编译预处理命令函数类型说明函数定义函数调用函数调用函数返回值形参实参
§ 5.3 变量的作用域和存储类别变量是对程序中数据的存储空间的抽象内存
…….
main()
{ int a;
a=10;
printf(“%d”,a);
} 102000
2001
程序中使用变量名对内存操作
变量的属性
数据类型:变量所持有的数据的性质( 操作属性 )
存储属性
存储器类型:寄存器、静态存储区、动态存储区
生存期,变量在某一时刻存在 -------静态变量与动态变量
作用域,变量在某区域内有效 -------局部变量与全局变量
变量的存储类型
auto -----自动型
register-----寄存器型
static ------静态型
extern -----外部型
变量定义格式,[存储类型 ] 数据类型 变量表 ;如,int sum;auto int a,b,c;
register int i;
static float x,y;
变量的作用域
程序中所声明的在程序的那一部分中是可用的。
只有在自己的作用域中才可用。
从作用域角度考虑分为,内部变量、外部变量
内部变量 ( 局部变量 ) 的作用域,是定义它的函数内或复合语句内,在它的作用域之外,内部变量是不可见的,也就是说,一个函数内定义的内部变量是不能被其它的函数所引用的 。
特性:有助实现信息隐蔽,即使不同的函数定义了同名的内部变量,也不会相互影响 。
内部变量 ---局部变量
定义:在 函数内定义,只在本函数内有效
说明:
main中定义的变量只在 main中有效
不同函数中同名变量,占不同内存单元
形参属于局部变量
可定义在复合语句中有效的变量
局部变量可用存储类型,auto register static
( 默认为 auto)
float f1(int a)
{ int b,c;
…….
}
char f2(int x,int y)
{ int i,j;
……
}
main()
{ int m,n;
…….
}
a,b,c有效
x,y,i,j有效
m,n有效复合语句中变量
#define N 5
main()
{ int i;
int a[N]={1,2,3,4,5};
for(i=0;i<N/2;i++)
{ int temp;
temp=a[i];
a[i]=a[N-i-1];
a[N-i-1]=temp;
}
for(i=0;i<N;i++)
printf("%d ",a[i]);
}
运行结果,5 4 3 2 1
例 不同函数中同名变量
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
外部变量 (全局变量)
定义,在 函数外面定义的变量。
外部变量的作用域,对于只有一个源程序文件构成的程序,外部变量的作用域是从定义它的位置开始,直至它所在源程序文件的结束。
特点,外部变量的使用增加了函数之间传递数据的途径,在外部变量的作用域内的任何函数都能引用该外部变量,一个函数对外部变量的修改,能影响到其它引用这个变量的函数;因此对外部变量的使用不当,会产生意外的错误 。
float max,min;
float average(int n)
{ int i; float x;
scanf(“%f”,&x);
max=min=x;
for(i=2;i<n;i++)
{scanf(“%f”,&x);
if(x>max) max=x;
else if(x<min) min=x;
sum+=x;}
return(sum/n);
}
main()
{ int n; float ave;
scanf(“%d”,&n);
ave=average(n);
printf("max=%6.2f\nmin=%6.2f\n
average=%6.2f\n",max,min,ave);
}
作用域
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(“a=%d\n,b=%d\n,max=%d\n",
a,b,max(a,b));
}
外部变量与局部变量同名时,在内部变量的作用域中,
外部变量被屏蔽。
例:
运行结果:
a=8 /*main中的 a*/
b=5 /*main中的 b*/
max=8
int i;
main()
{ void prt();
for(i=0;i<5;i++)
prt();
}
void prt()
{ for(i=0;i<5;i++)
printf(“%c”,’*’);
printf(“\n”);
}
外部变量使用不当时,会引起副作用。
运行结果,*****
外部变量的作用域可以通过关键字,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;
运行结果,max=13
extern int a,b;
int max()
{ int z;
z=a>b?a:b;
return(z);
}
main()
{ printf("max=%d",max());
}
int a=13,b=-8;
int p=1,q=5;
float f1(int a)
{ int b,c;
…….
}
int f3()
{…..
}
char c1,c2;
char f2(int x,int y)
{ int i,j;
……
}
main()
{ int m,n;
…….
}
c1,c2的作用范围
p,q的作用范围
extern char c1,c2;
c1,c2
的作用范围扩展后 c1,c2
的作用范围扩展后
extern char c1,c2;
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
int global;/*定义 */
extern float x;
/*说明 */
main()
{ int local;
.
.
.
}
extern int global; /*说明 */
int number; /*定义 */
func2()
{,
.
.
}
float x; /*定义 */
int number; /*定义 */
func3()
{ extern int global;
/*说明 */
.
.
.
}
file1.c
file2.c
file3.c
外部变量(全局变量)继续:
对于多文件程序,在一个源文件中定义的外部变量还可以在其它的文件中使用,这时需要在使用它的文件中用
extern进行 说明,这样的变量称为程序级的变量。
应尽量少使用全局变量,因为:
全局变量在程序全部执行过程中占用存储单元
降低了函数的通用性、可靠性,可移植性
降低程序清晰性,容易出错定义 说明
次数,只能 1次 可说明多次
位置,所有函数之外 函数内或函数外
分配内存,分配内存,可初始化 不分配内存,不可初始化
外部变量定义与外部变量说明不同:
在同一个文件中,定义在后使用在前的外部变量,在使用前需要对其进行声明。
在包含多个文件的程序中,一个文件若使用其它文件中定义的外部变量也要进行声明 。
外部变量说明:
extern 数据类型 变量表;
C语言中,按作用域的大小,可将变量的作用域分为四个级别,程序级,
文件级,函数级,复合语句级 。 这是由
C程序的结构特点所决定的 。
5.3.2 变量的存储类别 ------动态变量与静态变量
动态变量,动态存储类别的变量当进入定义它的函数或复合语句时被分配存储空间,当离开时所占内存空间被释放。
静态变量,静态存储类别的变量在源程序编译的时候被分配固定的存储空间,从程序开始执行到程序运行结束,一直占用该内存空间,直至程序运行结束,才被释放内存空间。
程序区静态存储区动态存储区全局变量、局部静态变量形参变量局部动态变量( auto register)
函数调用现场保护和返回地址等
生存期 ------变量存在(在内存有存储空间)的时间段。
静态变量,从程序开始执行到程序结束
动态变量,从包含该变量定义的函数开始执行至函数执行结束
5.3.3 内部变量的存储类别内部变量的作用域是定义它的函数或复合语句。内部变量的存储类别是指它存放的位置。内部变量可存放于内存的动态区,寄存器和内存的静态区。但无论内部变量存放在何处,它的作用域是不变的。
内部变量可以定义为,
自动的( auto)
寄存器( register)
静态的 ( static )
自动的( auto ):在函数内定义的变量都是自动的。
main()
{int a; /*等价于,auto int a;*/

}
寄存器变量( register )
如,register int i;
动态变量静态变量例 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作用域
静态的 ( static ):
如,static int i;
下次再调用该函数时,staic变量仍使用原来的存储单元,
仍使用原来存储单元中的值。
分配内存后、赋初值,并且只被赋初值一次,未赋值的内部 staic变量,系统自动给它赋值为 0。
被分配在内存的静态存储区中。
staic变量在内存的静态存储区占用的固定的内存单元;
即使它所在的函数被调用结束后,也不释放存储单元,它所在单元的值也会继续保留 -----因此:
虽然 staic变量在整个程序运行期间都是存在的,但在它的作用域外,它是不可见的,也就是说其它函数是不能引用它的。
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"
void test_a_s()
{auto int auto_v=0; /*内部自动的 */
static int static_v=0; /*内部静态的 */
printf("\nauto_v=%d,static_v=%d",auto_v,static_v);
auto_v++;
static_v++;
}
main()
{int i;
for(i=0;i<3;i++)
test_a_s();
}
例 5.8
运行结果,auto_v=0,static_v=0
auto_v=0,static_v=1
auto_v=0,static_v=2
利用静态变量的情况:
long func(long i)
{static long k=1;
k=k*i;
return (k);
}
main()
{long n,m;
printf("\n");
scanf("%ld",&n);
for (m=1L;m<=n;m++)
printf("\n%ld!=%ld",
m,func(m));
}
例 5.9计算 1!,2!,3!,……n!(n 的值由键盘输入 )
不利用静态变量的情况:
long func(long n)
{ long k=1; int i;
for(i=1;i<=n;i++)
k=k*i;
return (k);
}
main()
{long n,m;
printf("\n");
scanf("%ld",&n);
for (m=1L;m<=n;m++)
printf("\n%ld!=%ld",m,func(m));
}
5.3.4 外部变量的存储类别外部变量只能存放在内存的静态存储区 。它在整个程序的运行期间一直占用内存单元。
外部变量的作用域可以通过关键字,extern”来扩展。
(扩展至某个文件,甚至被其它源程序文件使用)
用 static声明的外部变量能够限制它的作用域的扩展,达到信息隐蔽的目的。
① 在同一个文件中,定义在后使用在前的外部变量,在使用前需要对其进行声明 。
② 在不同文件中 ( 但同属一个程序 ),使用其它文件中定义的外部变量 ( 但是不要企图使用 static声明了的外部变量 ) 。
/*p5-1002.c*/
int Max;
void func(int x,int y,int z)
{extern int Min;
Max=x;
Min=x;
if(y>Max) Max=y;
if(z>Max) Max=z;
if(y<Min) Min=y;
if(z<Min) Min=z;
}
int Min;
例 5.10 该 程序包括两个文件 p5-1001.c和 p5-1002.c,并且 文件
p5-1001.c使用 p5-1002.c中的外部变量,因此要在文件 p5-1001.c中对该外部变量进行声明。
/*p5-1001.c*/
#include"p6-1402.c"
extern int Max,Min;
main()
{void func(int x,int y,int z);
int a,b,c;
printf("\ a,b,c=?");
scanf("%d,%d,%d",&a,&b,&c);
func(a,b,c);
printf("\nMax=%d,Min=%d",Ma
x,Min);
}
变量存储类型静态动态存储方式程序整个运行期间函数调用开始至结束生存期编译时赋初值,只赋一次每次函数调用时赋初值自动赋初值 0或空字符不确定未赋初值静态存储区动态区存储区 寄存器局部变量 外部变量作用域 定义变量的函数或复合语句内 本文件 其它文件
局部变量默认为 auto型
register型变量个数受限,且不能为 long,double,float型
局部 static变量具有 全局寿命 和 局部可见性
局部 static变量具有 可继承性
extern不是变量定义,可扩展外部 变量作用域
register 局部 staticauto 外部 static 外部存储类别例 文件 file1.c
int a;
main( )
{ …….
…….
f2;
…….
f1;
…….
}
f1( )
{ auto int b;
………
f2;
……..
}
f2( )
{ static int c;
………
}
C作用域
b作用域
a作用域
main f2 f1main f1f2 main
a生存期,
b生存期,
c生存期,
例 变量的寿命与可见性
#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:-10register
main:c 0
静态存储区动态存储区
other,a 2
other,b 0
other,c 10
8
4
33
15
4
43
6
75
6
例 引用其它文件中的变量,输出 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);
}
5.4 内部函数和外部函数
5.4.1 外部函数外部函数是可以被程序中的其它文件所调用的函数。
定义格式如下:
extern 数据类型 函数名(形式参数表列

{ 说明部分;
执行部分;
}
外部函数是 C语言默认的函数类型,若没有特别的声明为 extern类型,系统也会默认为外部函数。
5.4.2 内部函数内部函数是只能被本文件中其它函数调用,而不能被其它文件调用的函数 。
它的定义格式如下:
static 数据类型 函数名 ( 形式参数表列 )
{ 说明部分;
执行部分;
}
§ 5.5 函数的嵌套与递归调用
嵌套调用
C规定,函数定义不可嵌套,但 可以嵌套调用 函数
main( )
调用函数 a
结束
a函数 b函数调用函数 b


例 求三个数中最大数和最小数的差值
#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函数例 用弦截法求方程根 080165 23 xxx
x
y
f(x)
0
x1
x2
x
f(x1)
f(x2)
)()(
)()(
12
1221
xfxf
xfxxfxx

求 f(x1)与 f(x2)连线与 x轴的交点 x
输入 x1,x2,求 f(x1),f(x2)
直到 f(x1)与 f(x2)异号
y=f(x),y1=f(x1)
y与 y1同号真 假
x1=x
y1=y
x2=x
直到 |y|<?
root=x 输出 root
root函数运行情况:Input x1,x2:
2,6?
A root of equation is 5.0000
main( )
调用函数 root
输出根 x
结束
root函数 xpoint函数调用函数 xpoint 调用函数 f
f函数
递归调用
定义:函数直接或间接的调用自身叫函数的递归调用
f( )
调 f 调 f2 调 f1
f1( ) f2( )
说明
C编译系统对递归函数的自调用次数没有限制
每调用函数一次,在内存堆栈区分配空间,用于存放函数变量、返回值等信息,所以递归次数过多,可能引起堆栈溢出
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);
}
例 求 n的阶乘



)1()!1(
)1,0(1!
nnn
nn
/*ch7_8.c*/
#include <stdio.h>
int fac(int n)
{ int 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,y;
printf("Input a integer number:");
scanf("%d",&n);
y=fac(n);
printf("%d! =%15d",n,y);
}
Hanoi(汉诺)塔问题:
古代有一个梵塔,塔内有三各底座 A、
B,C。开始时 A座上有 64个盘子,牌子大小不等,大的在下,小的在上。有一个老和尚想把这 64个盘子从 A座移到 C座上去,每一次只允许移动一个盘子,且在移动过程中每个底座上的盘子必须是大的在下,小的在上。
老和尚自然这样想:假若有另外一个和尚想办法将 63个盘子从一个地座上,那么问题就解决了。老和尚只需这样做:
( 1)命令第二个和尚将 63个盘子从 A移到
B座;
( 2)自己将最底下的最大的那一个盘子从
A移到 C座;
( 3)再命令第二个和尚将 63个盘子从 B移到 C座;
任务就完成了。
第二个和尚自然这样想:假若有第三个和尚想办法将 62个盘子从一个地座上,那么问题就解决了。第二个和尚只需这样做:
( 1)命令第三个和尚将 62个盘子从 A移到
C座;
( 2)自己将最底下的最大的那一个盘子从
A移到 B座;
( 3)再命令第二个和尚将 62个盘子从 C移到 B座;
这样第二个和尚的任务就完成了。
这样以此类推,层层下放,直到第 63个和尚找到第 64个和尚,第 64个和尚的任务是将一个盘子移到另一座上,第 63个和尚再完成自己的任务;第 62个和尚再完成自己的任务; … ;老和尚才能完成自己的任务。
以 3个盘子为例,步骤为:
A->C,A->B,C->B,A->C,B->A,B->C,A->C
例 5.14 Hanoi问题 void move(char getone,char putone){ printf("%c--->%c\n",getone,putone); }
void hanoi(int n,char one,char two,char three)
{ if(n==1) move(one,three);
else
{ hanoi(n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);
}
}
main()
{ int m;
printf("Input the number of disks:");
scanf("%d",&m);
printf("The steps to moving %3d disks:\n",m);
hanoi(m,'A','B','C');
}
A B C
§ 6.7 数组作为函数参数
数组元素作函数实参 ——值传递例 两个数组大小比较
4
3
2
1
0
5
a
56
23
12
10
76
88
4
3
2
1
0
5
b
21
23
43
98
66
54
n=0
m=0
k=0
i
n=0
m=0
k=1
i
n=0
m=1
k=1
i
n=1
m=1
k=1
i
n=1
m=1
k=2
i
n=2
m=1
k=2
i
n=3
m=1
k=2
a和 b为有 10个元素的整型数组比较两数组对应元素变量 n,m,k记录 a[i]>b[i],a[i]==b[i],
a[i]<b[i]的个数最后 若 n>k,认为数组 a>b
若 n<k,认为数组 a<b
若 n==k,认为数组 a==b
#include <stdio.h>
main()
{ 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("Enter array b:\n");
for(i=0;i<10;i++)
scanf("%d",&b[i]);
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;
}
/* Output */
}
int large(int x,int y)
{ int flag;
if(x>y) flag=1;
else if(x<y) flag=-1;
else flag=0;
return(flag);
}
数组名作函数参数
地址传递
在主调函数与被调函数分别定义数组,且类型应一致
形参数组大小 (多维数组第一维 )可不指定
形参数组名是 地址变量例 求学生的平均成绩
#include <stdio.h>
float average(int stu[10],int n);
void 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
例 数组元素与 数组名作函数参数比较
1
2
a
调用前
a[0]
a[1]
1
2
a
调用
a[0]
a[1]
1
2
x
y
2
1
x
y
交换
1
2
a
返回
#include <stdio.h>
void swap2(int x,int y)
{ int z;
z=x; x=y; y=z;
}
main()
{ int a[2]={1,2};
swap2(a[0],a[1]);
printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);
}
值传递
1
2
a
调用前
1
2
a
x
调用
2
1
a
x
交换
2
1
a
返回
#include <stdio.h>
void swap2(int x[])
{ int z;
z=x[0]; x[0]=x[1]; x[1]=z;
}
main()
{ int a[2]={1,2};
swap2(a);
printf("a[0]=%d\na[1]=%d\n",a[0],a[1]);
}
地址传递例 数组元素与 数组名作函数参数比较例 数组排序 ----简单选择排序
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 kj
j
j k
j k
j
j
j
j
j
9
49
i=0
例 数组排序 ----简单选择排序
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");
}
k
j
j k
j
kjj
j
j
j
0
1
2
3
4
5
6
7
8
9
a 49
68
57
32
9
99
27
13
76
88
array 9
49
k
k
13
68
i=1
0
1
2
3
4
5
6
7
8
9
a 9
13
27
32
49
57
68
76
88
99
array
i=8
例 数组排序 ----简单选择排序
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");
}
例 求二维数组中最大元素值
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=1
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=3
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=5
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12i
max=34
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]
例 求二维数组中各行元素之和
get_sum_row(int x[][3],int result[],int row,int col)
{ int i,j;
for(i=0;i<row;i++)
{ result[i]=0;
for(j=0;j<col;j++)
result[i]+=x[i][j];
}
}
main()
{ int a[2][3]={3,6,9,1,4,7};
int sum_row[2],row=2,col=3,i;
get_sum_row(a,sum_row,row,col);
for(i=0;i<row;i++)
printf("The sum of row[%d]=%d\n",i+1,sum_row[i]);
}
3
1 4
6
7
9
a sum_row
x
result
18
12