C程序设计
南京师范大学
地图学与地理信息系统 04级
专业选修课
主讲教师, 汪闽
回顾,数组
? 下标从 0开始,最大 n-1
? 数组的初始化方式非常灵活
? 字符数组比较特殊 (?\0?)
第七章 函数
7.1 概述
7.2 函数的定义
7.3 函数的返回值
7.4 函数的调用
7.5 函数参数及其传递方式
7.6 函数的嵌套与递归调用
7.8 变量的属性
7.7 数组作为函数参数
本章学习要点,
?函数的概念和定义方法
?函数中各种数据的传递方法和差别
?变量的作用域和存储类型 (特别是
static 变量的特点 )
?利用函数进行综合程序设计
7.9 内部函数与外部函数
源程序文件1
预编译命令
说明部分 执行部分
函数1 函数n
源程序文件i 源程序文件n
C程序
C程序结构
?C是 函数式 语言
?必须有且只能有一个名为 main的主函数
?C程序的执行总是 从 main函数开始,在 main中结束
?主函数调用其他函数,其他函数也可以相互调用
模块化程序设计语言,C语言7.1 概述
? 从用户角度
– 标准函数(库函数):由系统提供
– 用户自定义函数
? 从函数形式
– 无参函数
– 有参函数 *
函数基本分类 printf(…)scanf(…)
getchar()
putchar(…)
…
sort(…)
max(…)
message(…)
…int getch() 或
int getch(void)
int printf( const
char *format [,
argument]..,)
– 格式 合法标识符
函数返回值类型
缺省 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);
}
7.2 函数的定义
函数类型 函数名(形参表)
形参类型说明
{
说明部分
语句部分
}
传统风格, 例 有参函数(传统风格)
int max(x,y)
int x,y;
{ int z;
z=x>y?x:y;
return(z);
}
例 空函数
dummy( )
{ }
函数体为空 *
例 无参函数
printstar( )
{ printf(―**********\n‖); }
或
printstar(void )
{ printf(―**********\n‖); }
无参函数?
– 返回语句
? 形式,return (表达式 );
或 return 表达式 ;
或 return;
? 功能:使程序控制从被调用函数返回
到调用函数中,同时把返值带
给调用函数
? 说明:
– 函数中可有多个 return语句
– 若无 return语句,遇 } 时,
自动返回调用函数
– 若函数类型与 return语句中表达
式值的类型不一致,按前者为准,
自动转换 ------函数调用转换
– void型函数
7.3 函数的返回值 例 return 语句的示意
main()
{
int i=-4; int j=4;
int aa=test(i,j);
printf("%d",aa);
getch();
}
test (int a,int b)
{
if (a+b<0)
return a*b;
else if(a+b ==0)
return (a+b);
else {
printf("%d %d",a,b);
}
return;
}
例 无返回值函数
void swap(int x,int y )
{ int temp;
temp=x;
x=y;
y=temp;
}
(本函数有问题 !!)
*
7.4.1 调用形式
7.4 函数的调用
? 函数语句:
例 printstar();
printf(“Hello,World!\n”);
? 函数表达式:
例 m=max(a,b)*2;
? 函数参数:
例 printf(“%d”,max(a,b));
m=max(a,max(b,c));
? 对被调用函数要求:
– 必须是 已存在 的函数
– 库函数, #include <*.h>
– 用户自定义函数, 一般要加上 函数类型申明 (说明 )
? 函数申明 (示例 )
– 一般形式,函数类型 函数名 (形参类型 [形参名 ],…,,);
或 函数类型 函数名 ();
– 函数定义 与 函数申明 不同
– 作用:告诉编译系统函数类型、参数个数及类型,以便检验
– 函数申明位置,程序的数据申明部分(函数内或外)
– 下列情况下,可不作函数说明
?若函数返值是 int型,系统自动按 int型处理 (不推荐 !)
?被调用函数定义出现在主调函数之前
– 有些系统 (如 Borland C++)要求函数申明指出函数返值类型和形
参类型,并且对 void 和 int 型函数也要进行函数申明 *
7.4.2 函数申明 (说明 ) 例 函数的申明int test(int,int);
int test(int a,int b);
int test();
main(){
int i,j;
test();
}
int test (int a,int b){ }
int test(int,int);
例 函数的申明
int test (int a,int
b){ }
main(){
int i,j;
test();
}
?说明:
实参与形参 个数相等, 类型一致, 按顺序一一对应
若形参与实参类型不一致,自动按形参类型转换 —
——函数调用转换
形参在函数被调用前不占内存 ;函数调用时为形参分
配内存;调用结束,内存释放
实参表求值顺序,因系统而定( Turbo C 自右向左 )
?形式参数:定义函数时函数名后面括号中的变量名
?实际参数:调用函数时函数名后面括号中的变量或表达式
7.5 函数参数的传递方式
7.5.1 函数的实参与形参
main()
{ int a,b,c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf("Max is %d",c);
}
max(int x,int y)
{…}
形参
实参
7.5.2 按值传递
c=max(a,b); ( main 函数)
( max 函数)max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例 1 比较两个数并输出大者
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);
}
形参
实参
7 11x,y:调用前:
调用结束,7 11x,y:
/*ch7_2.c*/
#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 11a,b:
7 11x,y:
swap,7 11x,y:
11 7a,b:
temp
例 2 交换两个数
–方式:函数调用时,将数据的 存储地
址 作为参数传递给形参
–特点:
?形参与实参占用 同样 的存储单元
?,双向, 传递
?实参和形参必须是 地址 常量或变量
7.5.3 函数的地址传递
/*ch9_3.c*/
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);
文件包含编译预处理命令
函数类型说明
函数定义
函数调用
函数调用
函数返回值 *
形参
实参
例子解析
7.6.1 嵌套调用
C规定,函数定义不可嵌套,但 可以嵌套调用 函数
main( )
调用函数 a
结束
a函数 b函数
调用函数 b
?? ? ?
?
???
?
7.6 函数的嵌套与递归调用
main()
{
…
a();
…
}
a()
{
…
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函数
例 求三个数中最大数和最小数的差值
定义:函数直接或间接的调用自身叫函数的递归调用
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);
}
7.6.2 递归调用
例 1 求岁数:有 5个人坐在一起,问第 5个人几岁?他说比第 4个大 2岁。问第 4
个多少岁,比第 3个大 2岁,依次类推,第一个说 10岁,求第 5个人多大?
递推公式:
age(n) = age(n-1) + 2 (n>1)
age(1) = 10;
age( int n)
{
int c;
if(n= =1) return 10;
else
return age(n-1)+5;
}
main()
{
printf(“%d”,age(5));
}
源代码,(函数格式注意!)
age(5)
输出 age(5)
main
c=age(4)+2
age函数 n = 5
age (5) = 18
c=age(3)+2
age函数 n = 4
age (4) = 16
c=10
age函数 n = 1
age (1) = 10
…
递推公式:
age(n) = age(n-1) + 2 (n>1)
age(1) = 10;
??
?
???
??
)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);
}
例 2 求 n的阶乘
为了防止递归调用无终止
地进行,必须在函数内有终止
递归调用的手段。常用的办法
是加条件判断,满足某种条件
后就不再作递归调用,然后逐
层返回。
7.7 数组作为函数参数
7.7.1 数组元素作函数实参 ——值传递
例 两个数组大小比较
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);
}
?地址传递:实参、形参数组名是 地址变量
?在主调函数与被调函数分别定义数组,且类型应一致
?形参数组大小 (多维数组第一维 )可不指定
7.7.2 数组名作函数参数
#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
调用前
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");
}
例 数组排序
7.7.3 二维数组作为参数传递
max_value (int array[][4])
{
int i,j,k,max;
max=? ;
…
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));
}
参数传递方式
不能写成,
array[3][];
7.8.1 概述
? 变量是对程序中数据的存储空间的抽象 内存
…….
main()
{ int a;
a=10;
printf(―%d‖,a);
} 102000
2001
程序中使用变量名对内存操作
7.8 变量的属性
变量的属性
1)数据类型:变量所持有的数据的性质( 操作属性 );
2)按变量的作用域(空间范围),分为局部变量 (内部变
量 ),全局变量 (外部变量 );
3)按变量的存在的时间(时间范围),又可分为静态
存储方式和动态存储;
? 局部变量 ---内部变量
– 定义:在 函数内定义, 只在本函数内有效
– 说明:
?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有效
例 不同函数中同名变量
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);
}
例 复合语句中变量
#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
例 复合语句中变量
#define N 5
main()
{ int i;
t 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]);
}
例 不同函数中同名变量
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
7.8.2局部变量
与全局变量
? 外部变量
– 定义:在 函数外定义,可为 本文件其他函数共用
– 有效范围:从 定义变量的位置开始 到本源文件结束,及有
extern说明 的其它源文件
应尽量少使用全局变量,因为:
?全局变量在程序全部执行过程中占用存储单元
?降低了函数的通用性、可靠性,可移植性
?降低程序清晰性,容易出错
定义 说明
?次数,只能 1次 可说明多次
?位置,所有函数之外 函数内或函数外
?分配内存,分配内存,可初始化 不分配内存,不可初始化
>
>
> >
>
外部变量说明,extern 数据类型 变量表;
外部变量定义与外部变量说明不同
若外部变量与局部变量同名,则外部变量被屏蔽
外部变量可用存储类型,缺省 或 static
全局变量 --外部变量
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
例子解析( 1)
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;
extern char c1,c2;
c1,c2
的
作
用
范
围
扩
展
后 c1,c2
的
作
用
范
围*
扩
展
后
例子解析( 2)
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;
例 1 一个文件中的外部变量定义与说明
例 2 多个文件中的外部变量说明
方法:在一个文件中定义外部变量,在另一个文件中用 extern 对它进行申明
int A;
main()
{ … }
File1.c
extern int A;
main()
{ … }
File2.c
extern int A;
main()
{ … }
File3.c
外部变量定义 外部变量定义外部变量声名 *
/*ch7_17.c*/
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 *
例 外部变量与局部变量
? 存储方式
– 静态存储:程序运行期间分配固定存储空间
– 动态存储:程序运行期间根据需要动态分配存储空间
? 内存用户区
程序区
静态存储区
动态存储区
全局变量、局部静态变量
形参变量
局部动态变量( auto register)
函数调用现场保护和返回地址等
生存期
静态变量,从程序开始执行到程序结束
动态变量,从包含该变量定义的函数开始执行至函数执行结束
7.8.3 变量的存储类别:动态变量与静态变量
静态动态存储方式
程序整个运行期间函数调用开始至结束生存期
编译时赋初值,只赋一次每次函数调用时赋初值
自动赋初值 0或空字符不确定未赋初值
静态存储区动态区存储区 寄存器
局部变量 外部变量
作用域 定义变量的函数或复合语句内 本文件 其它文件
?局部变量默认为 auto型
?register型变量个数受限,且不能为 long,double,float型
?局部 static变量具有 全局寿命 和 局部可见性
?局部 static变量具有 可继承性
?extern不是变量定义,可扩展外部变量作用域 (已学)
register 局部 staticauto 外部 static 外部存储类别
变量存储类型
#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
例 变量的寿命与可见性
? 静态局部变量
1)在编译的时候赋初值,只赋一次,在程序运行时已经有初值;
2)如果定义变量时不给初值,则系统自动给一个初值,0(数值型),
空字符(字符型)
3)虽然在函数调用后依旧存在,但其他函数不能引用它;
? 用 static声明外部变量(注意和 extern 声明外部变量
的对比)
希望某些外部变量只限于被本文件引用,而不被其他文
件所引用
对 static变量作进一步的说明
static int A;
main()
{ … }
File1.c
extern int A;
main()
{ … }
File2.c
extern int A;
main()
{ … }
File3.c
7.9.1 内部函数(又称静态函数)
如果在一个源文件中定义的函数, 只能被本文件中的函数调用, 而不能被同
一程序其它文件中的函数调用, 这种函数称为内部函数 。
定义一个内部函数, 只需在函数类型前再加一个, static‖关键字即可, 如下
所示:
static 函数类型 函数名 (函数参数表 )
{…… }
关键字, static”,译成中文就是, 静态的,, 所以内部函数又称静态函数 。
但此处, static‖的含义不是指存储方式, 而是指对函数的作用域仅局限于本文件 。
使用内部函数的好处是:不同的人编写不同的函数时, 不用担心自己定义的
函数, 是否会与其它文件中的函数同名, 因为同名也没有关系 。
7.9 内部函数与外部函数
当一个源程序由多个源文件组成时,C语言根据函数能否被
其它源文件中的函数调用,将函数分为 内部函数 和 外部函数
。
7.9.2 外部函数
外部函数的定义:在定义函数时,如果没有加关键字
,static”,或冠以关键字, extern”,表示此函数是外部函数:
[extern] 函数类型 函数名 (函数参数表 )
{…… }
调用外部函数时, 需要对其进行说明:
[extern] 函数类型 函数名 (参数类型表 )[,函数名 2(参数类
型表 2)…… ];
[案例 7.11] 外部函数应用。
( 1)文件 mainf.c
main()
{
extern void input(… ),process(… ),output(… );
input(… ); process(… ); output(… );
}
( 2) 文件 subf1.c
……
extern void input(…… ) /*定义外部函数 */
{…… }
( 3) 文件 subf2.c
……
extern void process(…… ) /*定义外部函数 */
{…… }
( 4) 文件 subf3.c
……
extern void output(…… ) /*定义外部函数 */
{…… }
南京师范大学
地图学与地理信息系统 04级
专业选修课
主讲教师, 汪闽
回顾,数组
? 下标从 0开始,最大 n-1
? 数组的初始化方式非常灵活
? 字符数组比较特殊 (?\0?)
第七章 函数
7.1 概述
7.2 函数的定义
7.3 函数的返回值
7.4 函数的调用
7.5 函数参数及其传递方式
7.6 函数的嵌套与递归调用
7.8 变量的属性
7.7 数组作为函数参数
本章学习要点,
?函数的概念和定义方法
?函数中各种数据的传递方法和差别
?变量的作用域和存储类型 (特别是
static 变量的特点 )
?利用函数进行综合程序设计
7.9 内部函数与外部函数
源程序文件1
预编译命令
说明部分 执行部分
函数1 函数n
源程序文件i 源程序文件n
C程序
C程序结构
?C是 函数式 语言
?必须有且只能有一个名为 main的主函数
?C程序的执行总是 从 main函数开始,在 main中结束
?主函数调用其他函数,其他函数也可以相互调用
模块化程序设计语言,C语言7.1 概述
? 从用户角度
– 标准函数(库函数):由系统提供
– 用户自定义函数
? 从函数形式
– 无参函数
– 有参函数 *
函数基本分类 printf(…)scanf(…)
getchar()
putchar(…)
…
sort(…)
max(…)
message(…)
…int getch() 或
int getch(void)
int printf( const
char *format [,
argument]..,)
– 格式 合法标识符
函数返回值类型
缺省 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);
}
7.2 函数的定义
函数类型 函数名(形参表)
形参类型说明
{
说明部分
语句部分
}
传统风格, 例 有参函数(传统风格)
int max(x,y)
int x,y;
{ int z;
z=x>y?x:y;
return(z);
}
例 空函数
dummy( )
{ }
函数体为空 *
例 无参函数
printstar( )
{ printf(―**********\n‖); }
或
printstar(void )
{ printf(―**********\n‖); }
无参函数?
– 返回语句
? 形式,return (表达式 );
或 return 表达式 ;
或 return;
? 功能:使程序控制从被调用函数返回
到调用函数中,同时把返值带
给调用函数
? 说明:
– 函数中可有多个 return语句
– 若无 return语句,遇 } 时,
自动返回调用函数
– 若函数类型与 return语句中表达
式值的类型不一致,按前者为准,
自动转换 ------函数调用转换
– void型函数
7.3 函数的返回值 例 return 语句的示意
main()
{
int i=-4; int j=4;
int aa=test(i,j);
printf("%d",aa);
getch();
}
test (int a,int b)
{
if (a+b<0)
return a*b;
else if(a+b ==0)
return (a+b);
else {
printf("%d %d",a,b);
}
return;
}
例 无返回值函数
void swap(int x,int y )
{ int temp;
temp=x;
x=y;
y=temp;
}
(本函数有问题 !!)
*
7.4.1 调用形式
7.4 函数的调用
? 函数语句:
例 printstar();
printf(“Hello,World!\n”);
? 函数表达式:
例 m=max(a,b)*2;
? 函数参数:
例 printf(“%d”,max(a,b));
m=max(a,max(b,c));
? 对被调用函数要求:
– 必须是 已存在 的函数
– 库函数, #include <*.h>
– 用户自定义函数, 一般要加上 函数类型申明 (说明 )
? 函数申明 (示例 )
– 一般形式,函数类型 函数名 (形参类型 [形参名 ],…,,);
或 函数类型 函数名 ();
– 函数定义 与 函数申明 不同
– 作用:告诉编译系统函数类型、参数个数及类型,以便检验
– 函数申明位置,程序的数据申明部分(函数内或外)
– 下列情况下,可不作函数说明
?若函数返值是 int型,系统自动按 int型处理 (不推荐 !)
?被调用函数定义出现在主调函数之前
– 有些系统 (如 Borland C++)要求函数申明指出函数返值类型和形
参类型,并且对 void 和 int 型函数也要进行函数申明 *
7.4.2 函数申明 (说明 ) 例 函数的申明int test(int,int);
int test(int a,int b);
int test();
main(){
int i,j;
test();
}
int test (int a,int b){ }
int test(int,int);
例 函数的申明
int test (int a,int
b){ }
main(){
int i,j;
test();
}
?说明:
实参与形参 个数相等, 类型一致, 按顺序一一对应
若形参与实参类型不一致,自动按形参类型转换 —
——函数调用转换
形参在函数被调用前不占内存 ;函数调用时为形参分
配内存;调用结束,内存释放
实参表求值顺序,因系统而定( Turbo C 自右向左 )
?形式参数:定义函数时函数名后面括号中的变量名
?实际参数:调用函数时函数名后面括号中的变量或表达式
7.5 函数参数的传递方式
7.5.1 函数的实参与形参
main()
{ int a,b,c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf("Max is %d",c);
}
max(int x,int y)
{…}
形参
实参
7.5.2 按值传递
c=max(a,b); ( main 函数)
( max 函数)max(int x,int y)
{ int z;
z=x>y?x:y;
return(z);
}
例 1 比较两个数并输出大者
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);
}
形参
实参
7 11x,y:调用前:
调用结束,7 11x,y:
/*ch7_2.c*/
#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 11a,b:
7 11x,y:
swap,7 11x,y:
11 7a,b:
temp
例 2 交换两个数
–方式:函数调用时,将数据的 存储地
址 作为参数传递给形参
–特点:
?形参与实参占用 同样 的存储单元
?,双向, 传递
?实参和形参必须是 地址 常量或变量
7.5.3 函数的地址传递
/*ch9_3.c*/
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);
文件包含编译预处理命令
函数类型说明
函数定义
函数调用
函数调用
函数返回值 *
形参
实参
例子解析
7.6.1 嵌套调用
C规定,函数定义不可嵌套,但 可以嵌套调用 函数
main( )
调用函数 a
结束
a函数 b函数
调用函数 b
?? ? ?
?
???
?
7.6 函数的嵌套与递归调用
main()
{
…
a();
…
}
a()
{
…
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函数
例 求三个数中最大数和最小数的差值
定义:函数直接或间接的调用自身叫函数的递归调用
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);
}
7.6.2 递归调用
例 1 求岁数:有 5个人坐在一起,问第 5个人几岁?他说比第 4个大 2岁。问第 4
个多少岁,比第 3个大 2岁,依次类推,第一个说 10岁,求第 5个人多大?
递推公式:
age(n) = age(n-1) + 2 (n>1)
age(1) = 10;
age( int n)
{
int c;
if(n= =1) return 10;
else
return age(n-1)+5;
}
main()
{
printf(“%d”,age(5));
}
源代码,(函数格式注意!)
age(5)
输出 age(5)
main
c=age(4)+2
age函数 n = 5
age (5) = 18
c=age(3)+2
age函数 n = 4
age (4) = 16
c=10
age函数 n = 1
age (1) = 10
…
递推公式:
age(n) = age(n-1) + 2 (n>1)
age(1) = 10;
??
?
???
??
)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);
}
例 2 求 n的阶乘
为了防止递归调用无终止
地进行,必须在函数内有终止
递归调用的手段。常用的办法
是加条件判断,满足某种条件
后就不再作递归调用,然后逐
层返回。
7.7 数组作为函数参数
7.7.1 数组元素作函数实参 ——值传递
例 两个数组大小比较
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);
}
?地址传递:实参、形参数组名是 地址变量
?在主调函数与被调函数分别定义数组,且类型应一致
?形参数组大小 (多维数组第一维 )可不指定
7.7.2 数组名作函数参数
#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
调用前
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");
}
例 数组排序
7.7.3 二维数组作为参数传递
max_value (int array[][4])
{
int i,j,k,max;
max=? ;
…
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));
}
参数传递方式
不能写成,
array[3][];
7.8.1 概述
? 变量是对程序中数据的存储空间的抽象 内存
…….
main()
{ int a;
a=10;
printf(―%d‖,a);
} 102000
2001
程序中使用变量名对内存操作
7.8 变量的属性
变量的属性
1)数据类型:变量所持有的数据的性质( 操作属性 );
2)按变量的作用域(空间范围),分为局部变量 (内部变
量 ),全局变量 (外部变量 );
3)按变量的存在的时间(时间范围),又可分为静态
存储方式和动态存储;
? 局部变量 ---内部变量
– 定义:在 函数内定义, 只在本函数内有效
– 说明:
?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有效
例 不同函数中同名变量
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);
}
例 复合语句中变量
#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
例 复合语句中变量
#define N 5
main()
{ int i;
t 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]);
}
例 不同函数中同名变量
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
7.8.2局部变量
与全局变量
? 外部变量
– 定义:在 函数外定义,可为 本文件其他函数共用
– 有效范围:从 定义变量的位置开始 到本源文件结束,及有
extern说明 的其它源文件
应尽量少使用全局变量,因为:
?全局变量在程序全部执行过程中占用存储单元
?降低了函数的通用性、可靠性,可移植性
?降低程序清晰性,容易出错
定义 说明
?次数,只能 1次 可说明多次
?位置,所有函数之外 函数内或函数外
?分配内存,分配内存,可初始化 不分配内存,不可初始化
>
>
> >
>
外部变量说明,extern 数据类型 变量表;
外部变量定义与外部变量说明不同
若外部变量与局部变量同名,则外部变量被屏蔽
外部变量可用存储类型,缺省 或 static
全局变量 --外部变量
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
例子解析( 1)
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;
extern char c1,c2;
c1,c2
的
作
用
范
围
扩
展
后 c1,c2
的
作
用
范
围*
扩
展
后
例子解析( 2)
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;
例 1 一个文件中的外部变量定义与说明
例 2 多个文件中的外部变量说明
方法:在一个文件中定义外部变量,在另一个文件中用 extern 对它进行申明
int A;
main()
{ … }
File1.c
extern int A;
main()
{ … }
File2.c
extern int A;
main()
{ … }
File3.c
外部变量定义 外部变量定义外部变量声名 *
/*ch7_17.c*/
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 *
例 外部变量与局部变量
? 存储方式
– 静态存储:程序运行期间分配固定存储空间
– 动态存储:程序运行期间根据需要动态分配存储空间
? 内存用户区
程序区
静态存储区
动态存储区
全局变量、局部静态变量
形参变量
局部动态变量( auto register)
函数调用现场保护和返回地址等
生存期
静态变量,从程序开始执行到程序结束
动态变量,从包含该变量定义的函数开始执行至函数执行结束
7.8.3 变量的存储类别:动态变量与静态变量
静态动态存储方式
程序整个运行期间函数调用开始至结束生存期
编译时赋初值,只赋一次每次函数调用时赋初值
自动赋初值 0或空字符不确定未赋初值
静态存储区动态区存储区 寄存器
局部变量 外部变量
作用域 定义变量的函数或复合语句内 本文件 其它文件
?局部变量默认为 auto型
?register型变量个数受限,且不能为 long,double,float型
?局部 static变量具有 全局寿命 和 局部可见性
?局部 static变量具有 可继承性
?extern不是变量定义,可扩展外部变量作用域 (已学)
register 局部 staticauto 外部 static 外部存储类别
变量存储类型
#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
例 变量的寿命与可见性
? 静态局部变量
1)在编译的时候赋初值,只赋一次,在程序运行时已经有初值;
2)如果定义变量时不给初值,则系统自动给一个初值,0(数值型),
空字符(字符型)
3)虽然在函数调用后依旧存在,但其他函数不能引用它;
? 用 static声明外部变量(注意和 extern 声明外部变量
的对比)
希望某些外部变量只限于被本文件引用,而不被其他文
件所引用
对 static变量作进一步的说明
static int A;
main()
{ … }
File1.c
extern int A;
main()
{ … }
File2.c
extern int A;
main()
{ … }
File3.c
7.9.1 内部函数(又称静态函数)
如果在一个源文件中定义的函数, 只能被本文件中的函数调用, 而不能被同
一程序其它文件中的函数调用, 这种函数称为内部函数 。
定义一个内部函数, 只需在函数类型前再加一个, static‖关键字即可, 如下
所示:
static 函数类型 函数名 (函数参数表 )
{…… }
关键字, static”,译成中文就是, 静态的,, 所以内部函数又称静态函数 。
但此处, static‖的含义不是指存储方式, 而是指对函数的作用域仅局限于本文件 。
使用内部函数的好处是:不同的人编写不同的函数时, 不用担心自己定义的
函数, 是否会与其它文件中的函数同名, 因为同名也没有关系 。
7.9 内部函数与外部函数
当一个源程序由多个源文件组成时,C语言根据函数能否被
其它源文件中的函数调用,将函数分为 内部函数 和 外部函数
。
7.9.2 外部函数
外部函数的定义:在定义函数时,如果没有加关键字
,static”,或冠以关键字, extern”,表示此函数是外部函数:
[extern] 函数类型 函数名 (函数参数表 )
{…… }
调用外部函数时, 需要对其进行说明:
[extern] 函数类型 函数名 (参数类型表 )[,函数名 2(参数类
型表 2)…… ];
[案例 7.11] 外部函数应用。
( 1)文件 mainf.c
main()
{
extern void input(… ),process(… ),output(… );
input(… ); process(… ); output(… );
}
( 2) 文件 subf1.c
……
extern void input(…… ) /*定义外部函数 */
{…… }
( 3) 文件 subf2.c
……
extern void process(…… ) /*定义外部函数 */
{…… }
( 4) 文件 subf3.c
……
extern void output(…… ) /*定义外部函数 */
{…… }