第五章 函数
★ 内容提要:
函数的定义形式
函数的参数传递
函数的返回值与类型
函数间的数据联系
函数的递归调用
变量的存储类别与作用域
? 函数的定义
? 函数的参数传递
? 返回值与类型
? 函数间的数据联系
? 函数的递归调用
? 存储类别与作用域
C 程序由一个主函数 main() 和若干个其
它函数构成,执行时由主函数 调用其它
函数,其它函数可以互相调用,同一个
函数也可以被一个或多个函数 调用任意
多次。
第五章 函数
有
序
5, 2 函数的参数传递
形参可以是,简单变量
? 特点:一旦结合,即数据 数据传递完毕,实参与形
参不再有实际联系,即形参 在函数中的值的变化不会
改变主调函数的实参的原有值。
1,传值方式(单向传递)
实参可以是,常量、变量、表达式、数组元素
形参可以是,数组名、指针变量
? 特点:通过传递数组或某一存储单元的起始地址,
使被调函数可利用此地址来访问(存取)相应存储单
元的数据,实质上为通过存储单元共享,达到数据双
向传递的目的。
2,传地址方式(双向传递)
实参可以是,数组名、指针变量、字符串常量、
指定单元的起始地址等
注意:
函数调用时要求实参与形参在
个数、顺序、类型上必须匹配一致,
5, 2 函数的参数传递
5, 3 函数的返回值与函数类型说明
函数的类型应与 return 中的表达式类
型一致,函数类型决定返回值的类型
? 若 return 后面括号中的表达式为非整型,则必须在
函数名前冠以函数的类型 说明。函数类型决定返回值
的类型。
三点说明:
? 若不返回值,则用 void 类型定义函数。
void printmessage( ){
??
}
5, 3 函数的返回值与函数类型说明
函数的类型应与 return 中的表达式类
型一致,函数类型决定返回值的类型
三点说明:
? 一个函数可以有多个 return 语句。可采用形式,
return( 表达式 ) ; // 带值返回
return ; // 不带值返回
5, 3 函数的返回值与函数类型说明
函数的类型应与 return 中的表达式类
型一致,函数类型决定返回值的类型
三点说明:
5, 4 应遵守先定义后使用的调用规则
类型标识符 函数名(形参表);
若函数定义在后,调用在先,则应在主调函
数的定义说明部分给出被调函数的原型说明:
正确区分函数定义和函数 原型 说明:
定义, 是对函数功能的确立。
说明,是对已定义函数的函数名,函数类型,
形参的个数、顺序和类型等信息的说明。
5, 5 函数间的数据联系
? 通过 return 语句返回一个值
?
?
?
?
?
一个地址
或
一个数值
或
? 通过外部(全局)变量进行数据传递。
? 形实参数传递结合
?
?
?
?
?
)传地址方式(双向传递
或
传值方式(单向传递)
)传地址方式(双向传递
或
传值方式(单向传递)
5, 6 函数的递归调用
? 递归的概念,通俗地讲,用自身的结构来描述自身
就称为, 递归, 。这里指函数直接 或间接的自己调用
自己。
C 语言的函数除了 main() 函数,都可以递归调用,但
不能递归定义,即不能在函数内部定义 另一个函数,
但可以直接或间接地调用自己。
? 具有递归特性的问题,用递归调用描述它们就非常
方便。
C 语言的函数除了 main() 函数,都可以递归调
用,但不能递归定义,即不能在函数 内部定义
另一个函数,但可以直接或间接地调用自己。
? 构成递归的条件:
(1) 递归结束条件及结束时的值;
(2) 能用递归表达式形式表示,并且递归向结束条
件发展
例 [5 - 1]:
?
?
?
?
?
?
,递推公式不等于当
,递归结束条件当
求
0)!1(*
01
!
nnn
n
n
C 语言的函数除了 main() 函数,都可以递归调
用,但不能递归定义,即不能在函数 内部定义
另一个函数,但可以直接或间接地调用自己。
① 递归调用点, 此
时整个表达式的运算
还没有完成, 必须返
回一个值参与运算后,
求出整个表达式的值,
然后再返回上一层递
归调用点 。 递归调用
点也是返回点 。
② 保护现场:保存
数据, 以便返回时再
使用;保存返回地址
等
… els e ret urn( n*fac( n - 1 ));
fac(n)
fac(3)
fac(2)
fac(1)
fac(0)
… ret urn( n*fac( n - 1 ));
… ret urn( n*fac( n - 1 ));
… ret urn( n*fac( n - 1 ));
… return( 1 );
4
4
3
1
2
栈:先进后出。保存数据
int f ac(int n){
if (n==0) return(1);
els e return(n*fac(n - 1));
}
4
3
1
2
栈:先进后出。保存数据
1
2*1=2
1*1=1
4*6=24
1
2
6
24
… els e ret urn( n*fac( n - 1 ));
fac(n)
fac(3)
fac(2)
fac(1)
fac(0)
… ret urn( n*fac( n - 1 ));
… ret urn( n*fac( n - 1 ));
… ret urn( n*fac( n - 1 ));
… return( 1 );
4
…
…
…
…
…
3*2=2
递推方向
int f ac(int n){
if (n==0) return(1);
els e return(n*fac(n - 1));
}
运行结果, 输入,4 输出,24
print( n ) {
int i;
printf ("n= % - 5d \ n",n );
if(n <0) {
pu tchar(' - '); n = - n;
}
if(( i=n/10) != 0) p ri nt ( i );
pu tchar(n %10+'0');
}
void m ain() {
print( 123); pu tchar(' \ n');
}
例 [5 - 2],把整数 n 用十进制字符串的形式打印出来。
在执行过程中,每递归
调用一次,就引用一组
全新的自动变量,完全
独立于上层调用的变量
,即各层递归调用的变
量各自独立。可插入一
个打印语句给以证明。
运行结果,
n=123
n=12
n=1
123
C 程序中每一个变量和函数都有两个属性:
数据类型和存储类别。
5, 7 变量的存储类型及其作用域
数
据
类
型
char
short int
int long int
unsigned
float
double
……
数据类型确定
数据的存放形式
、值域及允许的
运算等。
数
据
类
型
……
数据类型确定
存储类别(确定存储区域)
存
储
类
别
auto 限定说明每次进入
函数时自动分配存储单元,
退出调函数时系统自动收
回存储单元的局部变量;
register 限定说明寄存
器变量;
extern 限 定 说 明 外 部
(全局)变量;
static 限定说明静态的
局部静态变量、局部静态
数组、静态外部变量等。
存
储
类
别
函数时自动分配存储单元,
退出调函数时系统自动收
限 定 说 明 外 部
局部静态变量、局部静态
内存中的用户区
用户程序区:
静态存储区:数据
的生存周期同程序
运行期,即变量能
保持到程序运行结
束
动态存储区:数据
(存储单元)随函
数的调用而存在,
随函数的返回而消
失(存储单元回收)
束
完整的变量定义形式:
说明,函数内定义的变量,若不指定存储类别,则作
auto 变量处理。函数外定义的变量为全局变量。
存储类别 数据类型 变量名表列;
main () {
static float a,b,c;
aut o d oub le x,y,z ; // 相当于 dou ble x,y,z ;
……
}
完整的函数定义形式:
说明,函数的存储类别只能使用 extern,static 这两
个限定词,以限制函数的调用范围。 省略存储类别隐
指外部函数,可供其它文件中的函数调用;静态函数
只能被本程序文件的函数调用。
存储类别 数据类型 函数名 ( 形参表 ){ …… }
static int m ax( in t x,in t y ) {
re tu rn( x> y? x, y );
}
提示:
在定义和说明变量时,应按照变
量的作用范围以及它们在存储单元
中保持值的时间长短的需要,对它
们的存储类别进行说明限定。
(1) 局部变量及其作用域
函数内部定义的变量统称为局部变量
,包括形参变量,由于存放在 动态区,
故也称为动态变量。
作用域, 本函数内有效
float f a(int a){
int b,c;
……
}
void m ain() {
int a,b,c;
……
{ int c;
c=a+b ;
……
}
……
}
a,b,c 作用域
局部变量
C 有效
在复合语
句内定义
的 C 有效
局部变量
C 有效
b,c
的
作
用
域
的
的
作
用
域
说明:
? 函数内定义
的变量只限本函
数内有效,
main() 函数中定
义的变量也只有
主函数内有效。
? 不同函数中
定义的变量可以
同名,各自代表
不同的对象,互
不干扰。
? 形参也是局
部变量。
? 没有指定类
别作 auto 处理。
? 内层定义的
局部变量优先。
? 局部动态变
量的初值不定。
float f a(int a){
int b,c;
……
}
void m ain() {
int a,b,c;
……
{ int c;
c=a+b ;
……
}
……
}
a,b,c 作用域
局部变量
C 有效
在复合语
句内定义
的 C 有效
局部变量
C 有效
b,c
的
作
用
域
的
的
作
用
域
(2) 寄存器变量及其作用域
为了提高执行效率,允许将局部变量
的值放在运算器的寄存器中,需要时直
接从寄存器取出参加运算。
说明, 只有局部自动变量和函数的形
参可以作为寄存器变量,并且类型只限
于 int, char 和指针类型。
int f ac(int n ){
re gister int i,f =1;
for( i=1; i<=n ; i++ )
f= f*i;
re tu rn( f );
}
void m ain() {
int i;
for( i=1; i<=5; i++ )
printf ( "%d != %3d \ n",i,fac(i) ) ;
}
例 [8 - 3],求 1! ~ 5! 的阶乘,
运行结果:
1!= 1
2!= 2
3!= 6
4!= 24
5!=120
(3) 外部变量及其作用域
在函数外定义的变量称为外部变量,即全局
变量,可以为本文件中其它函数所共用。其 有
效作用域,
从定义变量的位置开始到本源文件结束,
外部变量提供了不 同函数间进行
数据共享(传递)的另一途径。
float m ax=0,min =0; // 定义外部变量 max,min
float a ver age( float a rr ay[],int n ) {
int i; float a ver,sum =array[0];
max= min= arr ay[0] ;
for( i=1; i<n; i++ ) {
if( array[i]>m ax ) m ax=array[i];
if( array[i]<m in ) m in=array[i];
sum =sum +array[i];
}
aver =sum /n;
re tu rn(aver);
}
例 [5 - 4],用一维数组内存放 10 个学生成绩,写一个函
数,求出平均分、最高分和最低分。
void m ain() {
float a ve,sc ore [10] ; int i;
for( i=0; i<10; i+ + )
sc anf ("%f",& sc ore [i]) ;
ave=aver age( sc ore,10);
printf ("max =%f \ nm in=%f \ nav er ge=%6.2f \ n",
max,min,ave);
}
输入,9 9 45 7 8 9 7 1 0 0 6 7, 5 8 9 9 2 6 6 4 3
输出,m a x =1 0 0, 0 0
min= 43.00
averge= 77.65
fl oat max =0,mi n=0; // 定义外部变量 ma x,mi n
fl oat average(float array[],i nt n){
int i ; fl oat aver,sum=array[0];
max =mi n=a rray[0];
for(i =1; i <n; i+ +){
if (array[i] >max ) ma x =ar ray[i] ;
if (array[i] <mi n) mi n=array[i];
sum=s um+array[i] ;
}
aver=sum/ n; return(aver);
}
void mai n(){
fl oat ave,score[10]; i nt i;
for(i =0; i <10; i+ +)
scanf(" % f",&score[i ]);
ave=averag e( score,10);
printf("max= % f \ nmin= % f \ naverg e= % 6,2f \ n",
max,mi n,ave);
}
max,min
的有效
作用域
利
用
全
局
变
量
传
递
数
据
(4) 动态局部变量与静态局部变量的比较说明
动态局部变量
? 其作用域在空间上是
函数或复合语句的内部,
在时间意义上是函数的一
次调用周期或复合语句的
的执行周期;即进入函数
时系统自动为其分配存储
单元,函数调用结束时系
统自动收回该存储单元。
? 其初值不定。
静态局部变量
? 其值不随函数调用或
复合语句的执行结束而消
失,当再次进入函数或复
合语句时,它仍保持上次
函数调用时的值;即静态
局部变量在编译时只分配
一次存储单元,并一直保
持到程序结束。
? 有确定的初值,数值
为 0,字符为 ? \ 0 ? 。
其作用域在空间上是
其初值不定。
其值不随函数调用或
有确定的初值,数值
为 。
int fac(int n){
static int f=1;
f=f*n;
return(f);
}
void main(){
int i;
for(i=1; i<=5; i++)
printf("%d!=%3d \ n",i,fac(i));
}
例 [5 - 5],利用保留函数上一次调用结束时的值,求 1 到
5 的阶乘值。
程序区
静态区
动态区
1 i
1 f
1 n
说明:
① f 为静态局部变量,其值
不随函 数调用结束而消失
,保存至下一次被调 用时可
继续使用。
② 静态局部变量 f 的作用域
不变。仍为本函数内有效。
③ 利用静态局部变量的存
储特性,也属提高程 序运行
效率的一种有效手段。
int fac(int n){
static int f=1;
f=f*n;
return(f);
}
void main(){
int i;
for(i=1; i<=5; i++)
printf("%d!=%3d \ n",i,fac(i));
}
例 [5 - 5],利用保留函数上一次调用结束时的值,求 1 到
5 的阶乘值。
例 利用保留函数上一次调用结束时的值,求 到
的阶乘值。
int fac(int n){
static int f=1;
f=f*n;
return(f);
}
void main(){
int i;
for(i=1; i<=5; i++)
printf("%d!=%3d \ n",i,fac(i));
}
例 [5 - 5],利用保留函数上一次调用结束时的值,求 1 到
5 的阶乘值。
例 利用保留函数上一次调用结束时的值,求 到
的阶乘值。
运行结果,
1!= 1
2!= 2
3!= 6
4!= 24
5!=120
关于 static 的相关说明
? 静态函数:使函数局部化,限制其它 C 程序文件的
调用。
static 类型标识符 函数名 ( 形参表 ){ …… }
? 静态外部变量:函数外部定义的变量加 static 时,
将使外部变量局部化,限制其它源程序引用;
static 类型标识符 变量表列;
main(){ …… }
关于外部 ( 全局 ) 变量的相关说明
? 如果在定义点之前的函数或其它源程序文件中想引
用某外部变量,则应该在函数中用关键 字 extern 作,
外部变量, 说明。
? 使用全局变量会使函数的通用性及程序的清晰度降
低,要限制使用;
例 [5 - 6],综合情况。
int p=1,q=5;
float fa(int a){
int b,c;
……
}
char c1,c 2;
char f2(int x,int y){
int i,p;
……
}
……
void main(){
int m,n;
……
}
局部变量
a,b,c
局部变量
x,y,i,p
局部变量
m,n
全局变量
c1, c2,
全局变量
p 被屏蔽
全局变量
p
全局变量
p
q
例 [5 - 7],在定义点之前引用某外部变量的情况。
void f1(int a){
int b=0,c= 0; ex tern int p,q; // 声明全局变量 q,p
b++ ; c+ +; pri ntf(" % 4 d % 4d \ n",p,q);
}
char c1= 'A',c2= 'B';
void f2(int x,int y){
int i= 0,p=0; ex tern in t q; // 声明全局变量 q
i+ +; -- p; printf(" % 4 d % 4d \ n",p,q);
}
void main(){
int m=0,n=0; ex tern in t p,q;
f1(m); f2(m,n );
printf(" % 4d % 4d % 4d % 4 d % 4c % 4c \ n",p,q,m,n,c1,c2);
}
int p=1,q=5; // 定义全局变量 p,q
运行结果,
1 5
-1 5
1 5 0 0 A B
关于外部 ( 全局 ) 变量的相关说明
? 正确区分外部变量定义和说明:
? 定义, 只能一次,位置在函数之外,系统根据定
义分配存储单元,可以初始化;
? 说明, 可以多次,位置在函数之内,说明只是声
明该变量是一个已在外部定义过的变量,仅仅为引用
作声明。
int sum_prime(int r[]){
int i,j=499,sum=0; int prime(int n);
for(i=0; i<10; ){
if(prime(j)){ sum=sum+j; r[i++]=j;}
j -- ;
}
return(sum);
}
int prime(int n){
int i,k;
k=(int)sqrt((double)n);
for(i=2;i<=k;i++) if(n%i==0)return(0);
if(i>=k+1) return(1);
}
运行结果,
499 491 487 479
467 463 461 457
449 443
sum=4696
int stlen(char s[]){
int i=0;
while(s[i]!=' \ 0') i++;
return(i);
}
int stcmp(char s[],char t[]){
int i=0;
while(s[i]==t[i])
if(s[i++]==' \ 0') return(0);
return(s[i] - t[i]);
}
int stcpy(char s[],char t[]){
int i=0;
while((s[i]=t[i])!=' \ 0') i++;
return 0;
}
运行结果,
输入,aaaaaa
输出,aaaaaa
输入,aaaaaa
输入,bbbb
输出,bbbb
输入,bbbb
输入,abcde
输出,abcde
输入,abc
输出,abc
输入,ab
输出,ab
# include <stdio.h>
#include <math.h>
#define EPS 1e - 7
void main(){
double udf_sin(double x); // 用户自定义函数原型说明
double a; scanf("%lf",&a);
printf("%f %f \ n",udf_sin(a),sin(a));
}
double udf_sin( double x ){ // 用户自定义函数
double sum,term,n=1; sum=term=x;
while( fabs(term) > EPS ){
n=n+1;
term=term*( - x*x)/((2*n - 2)*(2*n - 1));Th e end
培 育 英 才 钻 研 科 学
书山有径勤为路
学海无边苦作舟
书山有径勤为路书山有径勤为路
学海无边苦作舟学海无边苦作舟