白 雪 飞
baixf@ustc.edu.cn
中国科学技术大学电子科学与技术系
Dept,of Elec,Sci,& Tech.,USTC
Fall,2003
第 5章 函 数
C语言程序设计 - 第 5章 函数 2
目 录
函数概述
函数定义的一般形式
函数的调用、参数和返回值
局部变量和全局变量
变量的存储类型
内部函数和外部函数
C语言程序设计 - 第 5章 函数 3
函数 (Function)
C语言用函数实现程序模块化
一个程序由一个或多个源程序文件组成
一个源程序文件由一个或多个函数组成
C程序的执行从 main函数开始,并回到
main函数结束
函数之间可以相互调用,或调用自身
不能调用 main函数
函数之间相互独立,不存在从属关系
C语言程序设计 - 第 5章 函数 4
函数定义的一般形式
函数定义形式
返回类型 函数名 (参数声明 )
{
声明部分语句
}
返回类型、参数、函数体内容都可没有
dummy(){}
/* does nothing & returns nothing */
C语言程序设计 - 第 5章 函数 5
函数定义形式的说明
关于“返回类型”
指函数返回值的类型
若省略此项,则认为返回类型是 int
若无返回值,则定义返回类型为 void
关于“参数声明”
类型 形参名,类型 形参名,...
关于“声明部分”
指变量、函数的声明
C语言程序设计 - 第 5章 函数 6
函数定义举例
int max(int x,int y)
{
int z;
z = x>y?x:y;
return z;
}
int min(int x,int y)
{
return x<y?x:y;
}
C语言程序设计 - 第 5章 函数 7
函数的调用、参数和返回值
函数的参数
形参和实参
参数传递
函数的返回值
函数的调用
函数调用的形式
函数的声明和函数原型
函数的嵌套调用和递归调用
C语言程序设计 - 第 5章 函数 8
函数的参数 (Arguments)
形式参数和实际参数
形参,定义 函数时,函数名后 ()中的参数
实参,调用 函数时,函数名后 ()中的参数
实参可以是常量、变量、表达式、函数调用
实参和形参必须类型相同或赋值兼容
举例
int max(int x,int y)/*函数定义 */
{ return x>y?x:y; }
max(a,3); /*函数调用 */
C语言程序设计 - 第 5章 函数 9
参数传递
值传递
实参对形参的数据传递是,值传递”
单向传递
只能把实参的值传递给形参
不能把形参的值传递给实参
对形参的值所作的改变不能带回给实参
实参和形参是不同的变量
具有不同的存储空间
具有不同的生存期和作用域
C语言程序设计 - 第 5章 函数 10
参数传递举例 (05-01.C)
void swap(int x,int y)
{
int t;
t=x,x=y,y=t;
}
void main()
{
int a=1,b=4;
swap(a,b);
}
1
a
4
b
1
x
4
y
4 1
C语言程序设计 - 第 5章 函数 11
return语句
形式
形式一,return;
形式二,return expression;
功能
函数返回语句
结束函数调用
如果需要,还可以带回函数返回值
C语言程序设计 - 第 5章 函数 12
函数的返回值 (Return Value)
返回值的说明
函数返回值的类型在函数定义中指定
函数返回值通过函数中的 return语句获得
若函数无返回值,则可以没有 return语句
return语句后的表达式类型,应与函数返回值类型相同,或可以兼容赋值;两者类型不同时,自动做类型转换
函数需要返回值时,若缺少 return语句,
或 return语句未带返回值,则返回一个不确定值
C语言程序设计 - 第 5章 函数 13
函数的调用
一般形式
函数名 (实参表列 )
说明
即使没有实参,()也不能省略
多个实参之间用逗号分隔
实参与形参按顺序一一对应,类型应匹配
实参的求值顺序不确定,应避免对此敏感的调用形式
printf("%d,%d",i,i++);
C语言程序设计 - 第 5章 函数 14
函数调用的应用
函数语句
printf("%d\n",i);
函数表达式
(c + d * max(a,b)) % e
函数参数
gcd(max(a,b),min(c,d))
说明
函数调用可以看作同返回类型的表达式使用
同时,函数调用还完成了函数中定义的操作
C语言程序设计 - 第 5章 函数 15
函数原型 (Prototype)
一般形式
类型 函数名 (类型 1,类型 2,...);
类型 函数名 (类型 1 形参 1,
类型 2 形参 2,...);
举例
int max(int,int);
int max(int x,int y);
void dummy(); /* 无参函数原型 */
void dummy(void); /* 同上 */
C语言程序设计 - 第 5章 函数 16
函数的声明 (Declaration)
函数声明的形式即函数原型
在函数调用之前,应对函数进行声明
函数声明可以在主调函数的声明部分,
也可以在函数外部
函数外部的声明,对所有主调函数都起作用,不需要在主调函数内再次声明
函数的定义也具有声明的作用
库函数的声明包括在头文件 (*.h)里,不需在源程序里声明
C语言程序设计 - 第 5章 函数 17
函数声明举例 (1)
void main()
{
int max(int x,int y); /* 函数声明 */
int a,b,c;
c = max(a,b);
}
int max(int x,int y)
{
return x>y?x:y;
}
C语言程序设计 - 第 5章 函数 18
函数声明举例 (2)
int max(int x,int y); /* 函数声明 */
void main()
{ /* 不需要再次声明 int max(int,int) */
int a,b,c;
c = max(a,b);
}
int max(int x,int y)
{
return x>y?x:y;
}
C语言程序设计 - 第 5章 函数 19
函数声明举例 (3)
int max(int x,int y)
/* 函数定义具有声明作用 */
{
return x>y?x:y;
}
void main()
{ /* 不需要再次声明 int max(int,int) */
int a,b,c;
c = max(a,b);
}
C语言程序设计 - 第 5章 函数 20
函数的嵌套调用
调用一个函数的过程中,调用另一函数
main函数结束调用 a函数
b函数a函数调用 b函数
C语言程序设计 - 第 5章 函数 21
函数的递归调用
一个函数直接或间接的调用它自身
必须有一定的条件判断语句,使得递归调用能够终止
f函数调用 f函数
f1函数调用 f2函数
f2函数调用 f1函数
C语言程序设计 - 第 5章 函数 22
函数递归调用举例
Hanoi塔 (汉诺塔、河内塔、梵塔 )
C语言程序设计 - 第 5章 函数 23
例 1,Hanoi塔
解法
N=1时,Hanoi(1)易解
若 Hanoi(N-1)可解,则 Hanoi(N)易解
Step1,N-1个盘子,借助 3,从 1移至 2
Step2,把最大的盘子从 1移至 3
Step3,N-1个盘子,借助 1,从 2移至 3
复杂度
移动次数,2N-1
N=64,移动次数~ 1.84467E+19
C语言程序设计 - 第 5章 函数 24
变量的作用域和生存期
作用域
变量有效的代码 空间
变量在作用域才能被引用
变量可以分为 局部变量 和 全局变量
生存期
变量有效的运行 时间
变量在生存期内才存在
变量可以分为 静态存储 和 动态存储
C语言程序设计 - 第 5章 函数 25
局部变量 (Local Variables)
局部变量
函数或复合语句内部定义的变量
说明
作用域为本函数或复合语句范围
不同函数内的局部变量可以重名,它们是不同的变量,作用域不同,不会相互干扰
函数形参也是局部变量的一种
若多个同名局部变量作用域重叠,则最内层复合语句中定义的局部变量有效
C语言程序设计 - 第 5章 函数 26
局部变量举例 (1)
float foo(int x)
{
int a,i,j; /* 局部变量 */
...,.,/* 只能用在 foo内部 */
}
void main()
{
int a,b,c; /* 局部变量 */
...,.,/* 只能用在 main内部 */
}
x,a,i,j
作用域
a,b,c
作用域
C语言程序设计 - 第 5章 函数 27
局部变量举例 (2)
void main()
{
int a,b,i;
...,..
{
int c,i;
c = a + b;
i = a - b;
...,..
}
...,..
}
a,b,i
作用域
c,i作用域此范围内复合语句中定义的 i有效
C语言程序设计 - 第 5章 函数 28
全局变量 (Global Variables)
全局变量 (外部变量 )
函数之外定义的变量
说明
作用域从变量定义处到本源程序文件结束
全局变量可以被本文件的函数所共用
增加函数间传递数据的渠道
若全局变量与局部变量重名,则局部变量在作用域内屏蔽全局变量
应限制使用过多全局变量
C语言程序设计 - 第 5章 函数 29
全局变量举例
int a=10,b=5,x,y; /* 全局变量 */
int max(int x,int y)
{
return x>y?x:y;
}
int c; /* 全局变量 */
void main()
{
int a=8;
c=max(a,b);
/* 引用局部变量 a */
}
全局变量
c作用域全局变量
a,b,x,y
作用域形参 x,y
作用域局部变量
a作用域
C语言程序设计 - 第 5章 函数 30
动态存储方式和静态存储方式
动态存储方式
程序运行期间根据需要动态分配存储空间
函数形参、自动变量等
每次调用函数时,给该函数中的局部变量和形参分配存储空间,函数返回后释放空间
静态存储方式
程序运行期间分配固定的存储空间
全局变量全部存放在静态存储区中
C语言程序设计 - 第 5章 函数 31
变量的存储类型
自动变量 auto
寄存器变量 register
静态变量 static
外部变量 extern
C语言程序设计 - 第 5章 函数 32
自动 (auto)变量
auto变量
局部变量,用 auto关键字修饰
auto可以省略,是缺省的存储方式
自动分配和释放存储空间
举例
int foo(int x)
{
auto int a,b,c=6; /* 定义自动变量 a,b,c */
float f,g=0.1; /* 定义自动变量 f,g */
}
C语言程序设计 - 第 5章 函数 33
寄存器 (register)变量
register变量
存放在 CPU寄存器中,存取速度快
寄存器变量只能动态分配存储空间 (寄存器 )
只有自动变量和形参可以作为寄存器变量
举例
int foo(int x)
{
register int i; /* 定义寄存器变量 i */
for(i=0; i<10000; i++)..,/* i用于循环变量 */
}
C语言程序设计 - 第 5章 函数 34
静态 (static)局部变量
静态局部变量
局部变量,用 static关键字修饰
静态局部变量为 静态存储,整个程序运行期间都不释放,其值不会丢失
在函数调用结束后,静态局部变量虽仍然存在,但已经不在变量的作用域内,所以其他函数不能引用它
静态局部变量在编译时赋初值,且只赋一次
若不对静态局部变量赋初值,则初值为 0
C语言程序设计 - 第 5章 函数 35
静态局部变量举例
int f(int a)
{
int b=0;
static int c=3;
b++; c++;
return a+b+c;
}
void main()
{
int x=2,i;
for(i=0;i<3;i++)
printf("%d\n",f(x));
}
i 0
x 2
a 2
b 0
c 3
f(x) -
12
1
456
789
C语言程序设计 - 第 5章 函数 36
用 extern声明外部变量
非静态外部变量
没有用 static声明的外部变量
可以用 extern声明外部变量,以扩展外部变量的作用域
可以在一个文件内声明外部变量,以在外部变量定义之前就可以使用
可以在多文件程序中声明外部变量,以在其他文件中引用某个文件中定义的外部变量
外部变量的定义一般放在所有函数之前
C语言程序设计 - 第 5章 函数 37
非静态外部变量举例 (1)
int max(int x,int y)
{
return x>y?x:y;
}
void main()
{
extern A,B;/* extern声明外部变量 */
printf("%d",max(A,B));
} /* 在定义之前使用外部变量 */
int A=13,B=-8; /* 定义外部变量 */
C语言程序设计 - 第 5章 函数 38
非静态外部变量举例 (2)
/* file1.c */
/* 声明外部变量 */
extern A,B;
void main()
{
int c;
c=max(A,B);
printf("%d",c);
}
/* file2.c */
/* 定义外部变量 */
int A=13,B=-8;
int max(int x,
int y)
{
int z;
z=x>y?x:y;
return z;
}
C语言程序设计 - 第 5章 函数 39
用 static声明外部变量
静态外部变量
限制外部变量只能被本文件引用
静态外部变量不能被其他文件引用
举例
/* file1.c */
static int A;
main()
{
...,..
}
/* file2.c */
extern int A;
void foo(int n)
{
A = A * n;
}
不能引用静态外部变量 A
C语言程序设计 - 第 5章 函数 40
变量的声明和定义
声明 (Declaration)
对变量的某些属性加以说明
定义 (Definition)
定义一个新的变量,并分配存储空间
定义有声明的功能,但声明不一定都是定义
举例
void main()
{
extern A; /* 声明而非定义,声明 A为已定义外部变量 */
...,..
}
int A; /* 定义 A为整型外部变量 */
C语言程序设计 - 第 5章 函数 41
从作用域角度分类
局部变量
自动变量 (离开函数,值就消失 )
寄存器变量 (离开函数,值就消失 )
形式参数 (可为自动或寄存器变量 )
静态局部变量 (离开函数,值仍保留 )
全局变量
静态外部变量 (只限本文件引用 )
非静态外部变量 (允许其他文件引用 )
C语言程序设计 - 第 5章 函数 42
从生存期角度分类
动态存储
自动变量 (本函数内有效 )
寄存器变量 (本函数内有效 )
形式参数 (本函数内有效 )
静态存储
静态局部变量 (本函数内有效 )
静态外部变量 (本文件内有效 )
非静态外部变量 (其他文件可以引用 )
C语言程序设计 - 第 5章 函数 43
从存放位置角度分类
内存动态存储区
自动变量
形式参数
内存静态存储区
静态局部变量
静态外部变量 (本文件内有效 )
非静态外部变量 (其他文件可以引用 )
CPU中的寄存器
寄存器变量
C语言程序设计 - 第 5章 函数 44
变量作用域和生存期小结变量存储类型 函数内 函数外作用域 生存期 作用域 生存期自动变量
形式参数寄存器变量静态局部变量
静态外部变量
非静态外部变量
只限本文件?其他文件也可以引用
C语言程序设计 - 第 5章 函数 45
内部函数
定义形式
static 类型 函数名 (形参表 )
说明
内部函数又称静态函数
内部函数只能在所在文件中使用
类似于静态外部变量的作用范围
举例
static int foo(int a,int b);
C语言程序设计 - 第 5章 函数 46
外部函数
定义形式
extern 类型 函数名 (形参表 )
说明
可以在其他文件中调用
extern关键字在定义时可以省略
需要调用该函数的文件中,用 extern声明该函数是外部定义的
举例
extern int foo(int a,int b);
C语言程序设计 - 第 5章 函数 47
各种变量和关键字用法总结
自动变量、形参、寄存器变量用法相同
寄存器变量存放在 CPU中,但不影响用法
所有外部变量都是 静态存储 的
不管是不是 static的外部变量
static关键字的意义
静态局部变量,表示,静态存储”
静态外部变量和函数,表示 只能用于本文件
非静态外部变量和函数,表示 其他文件可以引用,外部引用时用 extern加以声明
C语言程序设计 - 第 5章 函数 48
结束
The End