白 雪 飞
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