第五章 函数
★ 内容提要:
函数的定义形式函数的参数传递函数的返回值与类型函数间的数据联系函数的递归调用变量的存储类别与作用域
函数的定义
函数的参数传递
返回值与类型
函数间的数据联系
函数的递归调用
存储类别与作用域
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() 函数,都可以递归调用,但不能递归定义,即不能在函数 内部定义另一个函数,但可以直接或间接地调用自己。
① 递归调用点,此时整个表达式的运算还没有完成,必须返回一个值参与运算后,
求出整个表达式的值,
然后再返回上一层递归调用点 。 递归调用点也是返回点 。
② 保护现场:保存数据,以便返回时再使用;保存返回地址等
… el se 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 fac( int n){
if (n== 0) r eturn(1) ;
el se return( n*fac(n - 1));
}
4
3
1
2
栈:先进后出。保存数据
1
2*1=2
1*1=1
4*6=24
1
2
6
24
… el se 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 fac( int n){
if (n== 0) r eturn(1) ;
el se return( n*fac(n - 1));
}
运行结果,输入,4 输出,24
print ( n ){
int i;
print f( "n=% - 5d \ n",n );
if(n <0) {
pu tchar(' - '); n = - n;
}
if(( i=n/10) != 0) p rint( 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 () {
stat ic float a,b,c;
aut o d oub le x,y,z ; // 相当于 dou ble x,y,z ;
……
}
完整的函数定义形式:
说明,函数的存储类别只能使用 extern,static 这两个限定词,以限制函数的调用范围。 省略存储类别隐指外部函数,可供其它文件中的函数调用;静态函数只能被本程序文件的函数调用。
存储类别 数据类型 函数名 ( 形参表 ){ …… }
stat ic i nt max ( int x,int 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 i nt 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++ )
print f( "%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 verage ( f loat array[],int n ){
int i; float a ver,sum= array[0];
max= min= array[0];
for( i=1; i<n; i++ ) {
if( array[i]> max ) m ax=array[i];
if( array[i]< min ) m in=array[i];
sum =su m+ array[i];
}
aver=sum /n;
re tu rn(av er );
}
例 [5 - 4],用一维数组内存放 10 个学生成绩,写一个函数,求出平均分、最高分和最低分。
void m ain() {
float a ve,scor e[10]; int i;
for( i=0; i<10; i+ + )
scanf( "%f",&score[i]);
ave=average( score,10);
print f( "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 m ax =0,mi n=0; // 定义外部变量 ma x,mi n
fl oat average (fl oat array[ ],i nt n){
int i ; f loat aver,s um=arr ay[0];
max =mi n=a rray[0] ;
for( i= 1; i <n; i ++) {
if (array[ i] >max ) m a x =ar ray[i ];
if (array[ i] <mi n) mi n=array[ i] ;
sum= sum+ array[i ];
}
aver=s um/ n; return( aver);
}
void m ain( ){
fl oat ave,s core[10] ; i nt i ;
for( i= 0; i <10; i+ +)
scanf (" % f",&s core[i ]);
ave=average ( s core,10) ;
print f("max= % f \ nmi n= % f \ naverge= % 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,c2;
char f2(int x,int y){
int i,p;
……
}
……
void mai n(){
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 i nt p,q; // 声明全局变量 q,p
b++ ; c+ +; pr intf(" % 4 d % 4d \ n",p,q);
}
char c1= 'A',c 2= 'B' ;
void f2(int x,i nt y){
int i= 0,p= 0; ex tern i n t q; // 声明全局变量 q
i+ +; -- p; printf(" % 4 d % 4d \ n",p,q);
}
void mai n(){
int m= 0,n= 0; ex tern int p,q;
f1(m); f2(m,n);
printf(" % 4d % 4d % 4d % 4 d % 4c % 4c \ n",p,q,m,n,c1,c 2);
}
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));The end
培 育 英 才 钻 研 科 学书山有径勤为路学海无边苦作舟书山有径勤为路书山有径勤为路学海无边苦作舟学海无边苦作舟