1、第七讲 函数 (2),2,上讲内容回顾,一、模块化程序设计(7.1) 二、函数的定义(7.2) 三、函数的调用(7.3),3,本讲主要内容,7.3.4 函数的嵌套调用和递归函数 7.4 变量的作用域(Scope)与生存期 (Lifetime),4,7.3.4 函数的嵌套调用和递归函数,一、函数的嵌套调用 二、函数的递归调用,5,#include “stdio.h“ void f2( ) printf(“*n“); void f1(int n) int i;for(i=1;i=n;i+)f2(); void main( ) int n; printf(“请输入行数:“);scanf(“%d“,
2、,一、函数的嵌套调用,6,函数 B ,函数 A 调用函数 B; ,main函数 调用函数 A; ,一、函数的嵌套调用,C规定:函数定义不可嵌套,但调用可以嵌套。嵌套调用:在调用某函数的过程中调用另外一个函数。调用的执行过程:函数调用结束后返回到起点位置。,7,#include void main( ) int n=3;printf (“%dn“,sub1(n) ); ,sub1(int n) int i,a=0;for (i=n; i0; i-) a+=sub2(i);return a ;,sub2(int n) return n+1; ,程序输出结果: 9,嵌套调用举例:,8,【例】利用函数
3、嵌套,编写程序,求出所有的两位绝对素数。,分析: (1)问题描述:绝对素数是指本身是素数,其逆序数也是素数的数。例如:10321与12301是绝对素数。两位绝对素数是将一个素数的个位和十位交换位置后仍为素数。例如:11是两位绝对素数。 (2)需求分析和处理流程:用枚举法逐个判断所有两位数是否为绝对素数,输出绝对素数。,9,三要素: i=11;i=99;i+ 循环体: 判断1个数是否为两位绝对素数。 判断1个数是否为素数 个位和十位交换 再次判断交换后的数是否为素数设计自定义函数: 判断1个数是否为两位绝对素数 int absoluteprime (int n); 判断1个数是否为素数 int
4、prime (int n); 将这个数的个位和十位交换 int invert(int n);,10,#include #include int prime ( int n ) /*判断素数函数的定义*/ int j,flag=1;for( j=2; j=sqrt(n); j+)if(n%j=0) flag=0;break;return flag; ,int invert(int a) /*调换位置函数的定义*/ int m,n;m=a/10;n=a%10;return (n*10+m); ,11,void main() int n;for(n=11;n=99;n+)if( absolutepr
5、ime(n) ) printf(“%dt“,n); ,/*判断一个数是否为绝对素数*/ int absoluteprime (int n) /*函数定义*/ if(prime(n)=0) return 0;else if(prime(invert(n)=1) return 1;else return 0; ,12,数学中的递归定义。例如,求n的阶乘:要计算f(n)的值,就必须先算出f(n-1);要计算f(n-1)就必须先求出f(n-2);这样回推下去直到计算出f(0)时为止。由于f(0)已知,依此向回递归,计算出f(n)。,二、函数的递归调用,13,一个函数在它的函数体内调用它自身称为递归调用
6、。这种函数称为递归函数。 递归调用有两种: 直接递归:函数直接调用函数自身 间接递归:函数调用其它函数,而其它函数又调用该函数自身。,(1)直接递归,void fun( ), .,fun( );,.,(2)间接递归,void fun1( ), .,fun2( );,.,void fun2( ), .,fun1( );,.,二、函数的递归调用,14,无论是直接还是间接递归,两者都是无终止的调用自身。要避免这种情况的发生,使用递归解决的问题应满足两个基本条件:,(1)问题的转化。有些问题不能直接求解或难以求解,但它可以转化为一个新问题,这个新问题相对较原问题简单或更接近解决方法。这个新问题的解决与
7、原问题一样,可以转化为下一个新问题,。,(2)转化的终止条件。原问题到新问题的转化是有条件的、次数是有限的,不能无限次数地转化下去。确定递归算法的结束条件(或边界条件),这是决定递归程序能否正常结束的关键。,二、函数的递归调用,15,【例】用递归法设计求n!的函数。,分析: (1)用递归法计算n!,可用下述公式表示:,(2)用递归函数可将n!表示为:,16,#include double f(int n) double r;if(n0) printf(“n0,input error“);else if(n=0|n=1) r=1;else r=n*f(n-1);return r; void ma
8、in() int n;double y;printf(“ninput a inteager number:n“);scanf(“%d“, ,程序如下:,/*函数调用*/,/*递归终止条件*/,/*递归调用*/,17,void main ( ) y= f(4); ,递归调用执行情况如下:,double f(4) double r;r = 4*f(3);return r; ,double f(3) double r;r = 3*f(2);return r; ,double f(2) double r;r = 2*f(1);return r; ,double f(1) double r;return
9、 1; ,double f(int n) double r;if(n=0|n=1) r=1;else r=n*f(n-1);return r; ,18,斐波那契数列的递归方法实现。计算并输出斐波那契数列的前7个数据。,int fib(int n) if(n=0|n=1) return 1;else return fib(n-1)+fib(n-2); ,二、函数的递归调用,19,#include int fib(int n) if(n=0|n=1) return 1;else return fib(n-1)+fib(n-2); void main() int f,i;for(i=0;i7;i+)
10、 f=fib(i); printf(“%dt“,f); ,程序如下:,/*函数调用*/,/*递归终止条件*/,/*递归调用*/,20,7.4 变量的作用域(Scope)与生存期(Lifetime),二、变量的作用域局部变量和全局变量,三、存储类型动态存储与静态存储,一、变量的存储空间分配概念,21,思考:1. 何时为变量分配内存单元?2. 将变量分配在内存的什么区域?3. 变量占据内存的时间(生存期)?4.变量起作用的代码范围?,变量类型决定了变量在内存中所占的字节数及数据的表示形式,并且决定了变量起作用的代码范围,也决定了系统在什么时间、什么空间为变量分配或释放内存单元。这是变量的生存期和作
11、用域。,一、变量的存储空间分配概念,22,一、变量的存储空间分配概念,变量定义位置 变量的作用域 空间(变量的使用范围) 变量的存储类别变量的生存期 时间(变量的建立到消亡),23,作用域:指能正确访问该变量的有效程序范围。,二、变量的作用域 局部变量(local variable)与全局变量(global variable),24,main( ) int i=1 , j=3; printf(” %d n” , i+); int i=0; i+=j*2;printf (” %d, %dn”, i, j); printf (” %d, %d”, i, j);,程序输出结果为: 16,32,3,例
12、,函数体内定义的局部变量,复合语句内定义的局部变量,二、变量的作用域 局部变量(local variable)与全局变量(global variable),25,int m=13; int fun2( int x , int y ) int m=3; return (x*y-m); main( ) int a=7 , b=5; printf (” %d”, fun2(a, b)/m ); ,程序输出结果为: 2,全局变量,例,局部变量,二、变量的作用域 局部变量(local variable)与全局变量(global variable),26,#include int calculator(v
13、oid); void main(void) char op; int a,b,result;scanf(“%d%c%d“, ,【例】有实现简单四则算术运算器的程序如下,分析程序出错的原因。,int calculator(void) switch(op) case +: result=a+b;break;case -: result=a-b;break;case *: result=a*b;break;case /: if(b!=0) result=a/b;else printf(“error!“);return(result); ,变量op、a、b、result的作用域,变量op、a、b、re
14、sult未定义,不起作用,int calculator( char op,int a,int b ) int result;,局部变量,27,void swap( ) int t,x,y;t=x;x=y;y=t; main() int x=3,y=5;printf(“x=%d,y=%dn“,x,y);swap( );printf(“x=%d,y=%dn“,x,y); ,二、变量的作用域 局部变量(local variable)与全局变量(global variable),例,28,例 用全局变量实现两个数据的交换 void swap(int x,int y) int t;t=x;x=y;y=t
15、; main() int x=3,y=5;printf(“x=%d,y=%dn“,x,y);swap(x,y);printf(“x=%d,y=%dn“,x,y); ,二、变量的作用域 局部变量(local variable)与全局变量(global variable),29,例 用全局变量实现两个数据的交换 int x,y; void swap() int t;t=x;x=y;y=t; main() x=3,y=5;printf(“x=%d,y=%dn“,x,y);swap( );printf(“x=%d,y=%dn“,x,y); ,各个函数都可以对全局变量的值进行修改,使程序难控制,尽量少使
16、用全局变量。,二、变量的作用域 局部变量(local variable)与全局变量(global variable),30,例 用全局变量实现两个数据的交换 int x,y; void swap(int x,int y) int t;t=x;x=y;y=t; main() x=3,y=5;printf(“x=%d,y=%dn“,x,y);swap(x,y);printf(“x=%d,y=%dn“,x,y); ,结论: 全局变量与局部变量同名时,局部变量的作用域屏蔽全局变量,可以实现吗?,二、变量的作用域 局部变量(local variable)与全局变量(global variable),31
17、,局部变量同名不同函数中定义的局部变量(包括形参)可以同名,由于其作用域不同,因此互不影响。同一函数中不能定义两个同名的局部变量(包括形参) 全局变量与局部变量同名 当全局变量与某个函数中的局部变量同名时,在该函数内部局部变量的作用域将“覆盖”同名全局变量的作用域,关于变量同名,二、变量的作用域 局部变量(local variable)与全局变量(global variable),32,#include float max,min,ave; void count() int i; float sc;scanf(“%d“, ,【例】编写函数,统计10个成绩的最高分、最低分和平均分。(要求在mai
18、n函数中输出结果),33,从程序设计的观点看,使用全局变量 :,优点 增加了函数间的数据传递联系同一文件中的若干函数引用全局变量,可以在多个函数间传递变量值的变化。 函数通过全局变量可以得到多个返回值,缺点 全局变量在程序的全部执行过程中都占用存储单元。 使用全局变量过多,会降低程序的可读性和可维护性。所以要慎用、少用全局变量。 某函数对全局变量的引用出错,导致系统难以维护。,二、变量的作用域 局部变量(local variable)与全局变量(global variable),34,三、存储类别动态存储与静态存储,变量的存储类别 变量的存储类别反映了数据在内存中的存储方式。 变量按作用域可分
19、为局部变量和全局变量;按其存在的时间(生存期)分为静态存储变量和动态存储变量。,动态存储变量: 程序运行期间根据需要临时分配存储空间的变量。 特点:函数开始调用时为变量分配存储空间,函数结束时释放这些变量空间。 静态存储变量 程序运行期间“永久”占用固定内存的变量。 特点:在静态存储区为变量分配存储单元,整个程序运行期间都不释放。,35,C语言变量存储分类符 auto自动存储类型 static静态存储类型 extern外部存储类型 register寄存器存储类型,如:auto int a;static int b; register int d;,定义变量的一般形式:存储分类说明 类型说明 变
20、量名;,三、存储类别动态存储与静态存储,36,1. auto(自动)存储类别变量,只能说明局部变量, 函数内凡未加存储类型说明的变量均视为自动变量,自动变量说明必须在一个函数体内或复合语句中。随函数的调用而存在,随函数的返回而消失,它们在一次调用结束到下一次调用开始之间不再占有存储空间。,例:int kv(int a) auto int x,y; /*auto可省*/ auto char c; /*c的作用域*/ /*a,x,y的作用域*/,37,局部静态变量,全局静态变量,函数内部定义,作用域在函数内部,当所在的函数执行结束后,静态变量所占内存单元并不释放,其值仍然保留,直到整个程序结束。,
21、函数外部定义, 作用域仅限所在的源程序文件(.c),是指在编译时分配存储空间的变量,整个程序运行期间静态变量所占的存储单元不释放,其值不会丢失。对不赋初值的静态变量,初值自动为0。 可以说明全局变量,也可以说明局部变量。,2、static(静态)存储类别变量,38,fun( ) int a=0;a+=2;printf(“%d”, a); main( ) int cc;for( cc=1 ; cc=4 ; cc+ ) fun( );,结果是:2222,结果是:2468,例,局部静态变量,2、static(静态)存储类别变量,39,【例】在函数中使用static存储类别实现输出九九乘法表。,40,
22、void Mul(void) static int a=1; int i;for (i=1;i=a;i+) printf(“ %d*%d=%2d “,a,i,a*i); printf(“n“);a+; void main(void) int b; for(b=1;b=9;b+) Mul( ); ,41,int x,y; void num() int a=15, b=10;x=a-b; y=a+b; main() int a=7, b=5;x=a+b; y=a-b;num();printf(“%d,%dn”,x,y); ,例,3. extern(外部)存储类别变量,说明:全局变量的定义省略存储类
23、说明时,默认为extern。,42,void num() int a=15, b=10;x=a-b; y=a+b; int x,y;main() int a=7, b=5;x=a+b; y=a-b;num();printf(“%d,%dn”,x,y); ,例,可以吗?,3. extern(外部)存储类别变量,43,全局变量的作用域是从定义点到本文件结束。如果定义点之前的函数需要引用这些全局变量时,需要在函数内对被引用的全局变量进行说明。其一般形式为: extern 类型说明符 变量名,变量名. 其中方括号内的extern定义时可以省去不写, 声明变量时不能省。,3. extern(外部)存储类
24、别变量,44,void num() extern int x,y; /*外部变量说明*/int a=15, b=10;x=a-b; y=a+b; int x,y;main() int a=7, b=5;x=a+b; y=a-b;num();printf(“%d,%dn”,x,y); ,例,3. extern(外部)存储类别变量,45,变量,(按使用的存储媒介),内存变量,寄存器变量,使用CPU中的寄存器来存取变量的值,前面所介绍的变量都为内存变量,由编译程序在内存中分配存储单元,变量的存取在内存储器中完成,寄存器处在CPU的内部,可避免CPU频繁访问存储器,从而提高程序的执行速度,4.regi
25、ster(寄存器)变量,46,寄存器型变量,与auto型类似,运行速度快。,不能定义为全局变量,只限于整型、字符型和指针型局部变量。,不可以定义为静态局部变量,只能为动态变量。,register 类型名 变量名;,寄存器型变量的定义形式:,register int a; register char b;,4.register(寄存器)变量,47,当程序对运行速度有较高要求时,把那些频繁使用的少数变量指定为register变量。,例 编写程序计算s=1+4+9+100#include “stdio.h“void main(void)register int i,s=0;for(i=1;i=10;
26、i+) s=s+i*i;printf(“s=%dn“,s);,4.register(寄存器)变量,48,变量定义位置 变量的作用域 空间(变量的使用范围),小结,49,变量 (按生存期),静态存储 变量,动态存储 变量,extern和static类型。 生存期为程序执行的整个过程,在该过程中占有固定的存储空间,也称永久存储。,Auto和register类型。 当程序调用该变量时,才分配存储空间,执行完后,变量的存储空间被释放。,小结,变量的存储类别变量的生存期 时间(变量的建立到消亡),50,本讲小结,7.3.4 函数的嵌套调用和递归函数 7.4 变量的作用域(Scope)与生存期 (Lifetime),51,The End thanks,52,f( ) int b=5; static int a=3;printf(” %d,%d n”,+a,+b); main( ) int a=1 , b=2 , k;for(k=1;k4;k+) f( );printf(” %d,%d”, a , b ); ,结果是:4,65,66,61,2,例,局部静态变量,