




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第5章 函数与程序结构,C语言大学实用教程,5.1程序设计的艺术性 5.2函数的定义与使用 5.3函数的作用域和存储类型 5.5预处理命令,本章要点 函数的定义方法及函数的类型和返回值。 函数实参与形参,参数传递的方式。 函数的正确调用,嵌套与递归调用。 局部变量和全局变量的概念和使用方法。 本章难点 函数参数的传递与返回值 。 函数递归调用的执行过程。 变量的作用域和存储类型。,5.1 程序设计的艺术性,结构化程序设计有两大最高级的艺术 算法设计艺术 结构设计艺术 C语言为程序的结构提供了两样武器 函数和模块,函数(function)是结构设计的最基本单位 一个C程序由一个主函数和若干个函数
2、组成,由主函数调 用其他函数,其他函数之间也可以相互调用。同一个函数 可以被一个或多个函数调用任意多次。,5.1 程序设计的艺术性,C程序结构,C是模块化程序设计语言,特点: (1) 一个源程序文件由一个或多个函数组成。一个源程序文件 是一个编译单位,即以源程序为单位进行编译,而不是以函数 为单位进行编译。 (2) 一个程序由一个或多个源程序文件组成。一个源文件可 以为多个C程序公用。 (3) 程序的执行从main函数开始,调用其他函数后流程回到 main函数,在main函数中结束整个程序的运行。main函数是 系统定义的。 (4) 所有函数都是平行的,即在定义函数时是互相独立的,一 个函数并
3、不从属于另一函数,即函数不能嵌套定义,函数间可 以互相调用,但不能调用main函数。main函数是系统调用的.,5.1 程序设计的艺术性,5.2 函数的定义与使用,5.2.1 函数的分类 5.2.2 函数的定义 5.2.3 函数的调用、参数和返回值 5.2.4 函数原型 5.2.5 函数main()的特殊性,从用户使用的角度看,函数有两种: 标准函数,即库函数。这是由系统提供的,用户不必自己定 义这些函数,可以直接使用它们。应该说明,不同的C系统提 供的库函数的数量和功能不同,当然有一些基本的函数是共同 的。 用户自己定义的函数。用以解决用户的专门需要。 从函数的形式看,函数分两类: 无参函数
4、。在调用无参函数时,主调函数并不将数据传送 给被调用函数,一般用来执行指定的一组操作,无参函数可以 带回或不带回函数值,但一般以不带回函数值的居多。,5.2.1 函数的分类,有参函数。在调用函数时,在主调函数和被调用函数之间 有数据传递。也就是说,主调函数可以将数据传给被调用函数 使用,被调用函数中的数据也可以带回来供主调函数使用。,例5.1 #include”stdio.h” void main() void p_star(); void p_message(); p_star();* 调用p_star函数 * p_message( );/* 调用p_message */ p_star ()
5、;* 调用p_star函数 * ,5.2.1 函数的分类,void p_star() *定义p_star函数* printf(* * * * * * * * * * * * * * * * * *n); void p_message() *定义p_message函数* printf(How do you do!n); 运行情况如下: * * * * * * * * * * * * * * * * * * How do you do! * * * * * * * * * * * * * * * * * *,5.2.1 函数的分类,5.2.2 函数的定义,函数应该先定义后使用。 任何函数(包括主函
6、数main())都是由函数头和函数体两 部分组成。函数头给出函数相关信息(类似“黑匣子”中的入口 和出口),而函数体具体实现函数的功能。 函数定义的一般形式 (1) 无参函数的定义 类型标识符函数名() 声明部分 语句 用“类型标识符”指定函数值的类型,即函数带回来的值的类型。 无参函数一般不需要带回函数值,因此可以不写类型标识符。,合法标识符,函数返回值类型 缺省int型 无返回值void,函数体,类型标识符 函数名(形式参数说明 形式参数表) 说明部分 语句部分 ,例 无参函数 p_star( ) printf(“*n”); 或 p_star(void ) printf(“*n”); ,(
7、2)有参函数定义的一般形式,5.2.2 函数的定义,例如: int max(int x,int y) int z;*函数体中的声明部分 zxy?x:y; return(z); 在函数体的语句中求出的值(为x与y中大者),return(z)的 作用是将z的值作为函数值带回到主调函数中。return后面的括 弧中的值(z)作为函数带回的值(或称函数返回值)。在函数定 义时已指定max函数为整型,在函数体中定义为整型,二者 是一致的,将作为函数max的值带回调用函数。如果在定义 函数时不指定函数类型,系统会隐含指定函数类型为int型。因 此上面定义的max函数左端的int可以省写。,5.2.2 函数
8、的定义,(3)可以有“空函数” 它的形式为 类型说明符 函数名() 例如: dummy() 调用此函数时,什么工作也不做,没有任何实际作 用。在主调函数中写上“dummy();” 表明 “这里 要调用一个函数”, 而现在这个函数没有起作用, 等以后扩充函数功能时补充上。,5.2.2 函数的定义,5.2.3 函数的调用、参数和返回值,一、函数的调用 函数调用的一般形式为: 函数名(实参表列); 说明: (1)调用函数时,函数名称必须与具有该功能的自定义函数 名称完全一致。如果是调用无参函数则实参表列可以没有,但 括弧不能省略。 (2)实际参数表中的参数(简称实参),可以是常数、变量 或表达式。如
9、果实参不止1个,则相邻实参之间用逗号分隔。 (3)实参的个数、类型和顺序,应该与被调用函数所要求的 参数个数、类型和顺序一致 (4)对实参表求值的顺序并不是确定的。Turbo C规定是自 右至左顺序求值。,【例5.2 】 #include void main() int i=2,s; s=f(i,+i); /*函数调用*/ printf(%d,s); int f(int x,int y) /*函数定义*/ int z; if(xy) z=1; else if (x=y) z=0; else z =-1; return(z);,5.2.3 函数的调用、参数和返回值,在Turbo C系统上运行的结
10、果为:0 如果按自左至右顺序求实参的值,则函数调用相当于f(2,3),程序运行应得结果为“1”。若按自右至左顺序求实参的值,则它相当于f(3,3),程序运行结果为“0”。由于存在上述情况,使程序通用性受到影响。因此应当避免这种容易引起不同理解的情况。 如果本意是按自左而右顺序求实参的值,可以改写为 i;ki;pf(,k); 如果本意是自右而左求实参的值,可改写为 ji;pf(j,j); 这种情况在printf函数中也同样存在,如 printf(d,d,i,i); 也发生上述同样的问题,若i的原值为3,在Turbo C上运行结 果为4,3。,5.2.3 函数的调用、参数和返回值,例5.3a 计算
11、两个整数的平均数,/* 函数功能: 计算平均数 函数入口参数: 整型x,存储第一个运算数 整型y,存储第二个运算数 函数返回值: 平均数 */ int Average(int x, int y) int result; result = (x + y) / 2; return result; ,例5.3b 使用了Average函数的main(),main() int a = 12; int b = 24; int ave; ave = Average(a, b); printf(Average of %d and %d is %d.n, a, b, ave); ,函数调用的方式 按函数在程序中
12、出现的位置来分,可以有以下三种函数 调用方式: 1) 函数语句 把函数调用作为一个语句。如例5.1中的p_star(); 这时不要求函数带回值,只要求函数完成一定的操作. 2) 函数表达式 函数出现在一个表达式中,这种表达式称为函数表达 式。这时要求函数带回一个确定的值以参加表达式的 运算。例如: c2*max(a,b);函数max是表达式的一部分, 它的值乘2再赋给c。,5.2.3 函数的调用、参数和返回值,3) 函数参数 函数调用作为一个函数的实参。 例如: mmax(a,max(b,c); 其中max(b,c)是一次函数调用,它的值作为 max另一次调用的实参。m的值是a、b、c三者最大
13、 的。 又如:printf (“%d”, max (a,b); 也是把max(a,b)作为printf函数的一个参数。 函数调用作为函数的参数,实质上也是函数表达式形 式调用的一种,因为函数的参数本来就要求是表达式 形式。,5.2.3 函数的调用、参数和返回值,二、参数 在定义函数时函数名后面括弧中的变量名称为(“形参”), 在调用函数时,函数名后面括弧中的表达式称为“实际参数” (简称“实参”)。,5.2.3 函数的调用、参数和返回值,例 比较两个数并输出大者,main() int a,b,c; scanf(%d,%d, ,形参和实参的功能是作数据传送。发生函数调用时,主调函 数把实参的值传
14、送给被调函数的形参从而实现主调函数向被调 函数的数据传送。 说明: 实参必须有确定的值 形参必须指定类型 形参与实参类型一致,个数相同 若形参与实参类型不一致,自动按形参类型转换 函数调用转换 形参在函数被调用前不占内存;函数调用时为形参分 配内存;调用结束,内存释放,5.2.3 函数的调用、参数和返回值,例 计算x的立方,#include float cube(float x) return(x*x*x); main() float a, product; printf(Please input value of a:); scanf(%f, ,x,1.2,1.2,1.728,5.2.3 函
15、数的调用、参数和返回值,参数传递方式 值传递方式 方式:函数调用时,为形参分配单元,并将实 参的值复制到形参中;调用结束,形参单元 被释放,实参单元仍保留并维持原值 特点: 形参与实参占用不同的内存单元 单向传递,5.2.3 函数的调用、参数和返回值,例 交换两个数,#include void main() int x=7,y=11; printf(x=%d,ty=%dn,x,y); printf(swapped:n); swap(x,y); printf(x=%d,ty=%dn,x,y); swap(int a,int b) int temp; temp=a; a=b; b=temp; ,5
16、.2.3 函数的调用、参数和返回值,地址传递 方式:函数调用时,将数据的存储地址作 为参数传递给形参 特点: 形参与实参占用同样的存储单元 “双向”传递 实参和形参必须是地址常量或变量,5.2.3 函数的调用、参数和返回值,swap(p1,p2) int *p1,*p2; int p; p=*p1; *p1=*p2; *p2=p; #include void main() int a,b; scanf(%d,%d, ,例 交换两个数,5.2.3 函数的调用、参数和返回值,三、函数的返回值 功能:使程序控制从被调用函数返回到调用函数中,同时 把返值带给调用函数 形式: return(表达式);
17、或 return 表达式; 或 return; 下面对函数值作一些说明: (1) 函数的返回值是通过函数中的return语句获得的。return 语句将被调用函数中的一个确定值带回主调函数中去。如 果需要从被调用函数带回一个函数值(供主调函数使用), 被调用函数中必须包含return语句。如果不需要从被调用函 数带回函数值可以不要return语句。,5.2.3 函数的调用、参数和返回值,一个函数中可以有一个以上的return语句,执行到哪一个 return语句,哪一个语句起作用。 return语句后面的括弧也可以不要。 如return ;它与“return();”等价。 return后面的值可
18、以是一个表达式。 例如,上例中的函数max可以改写如下: max(int x,int y) return(xy?xy); 这样的函数体更为简短,只用一个return语句就把求值和返回 都解决了。,5.2.3 函数的调用、参数和返回值,(2) 函数值的类型。既然函数有返回值,这个值当然应属于某 一个确定的类型,应当在定义函数时指定函数值的类型。 例如: int max(float x,float y)/* 函数值为整型 */ char letter(char c1,char c2) /* 函数值为字符型 */ double min(int x,int y) /* 函数值为双精度型 */ 语言规定
19、,凡不加类型说明的函数,一律自动按整型处理。 在定义函数时对函数值说明的类型一般应该和return语句中的 表达式类型一致。 (3) 如果函数值的类型和return语句中表达式的值不一致,则 以函数类型为准。对数值型数据,可以自动进行类型转换。即 函数类型决定返回值的类型。,5.2.3 函数的调用、参数和返回值,例5.3 #include”stdio.h” void main() float a,b; int c; scanf(“%f,%f”, ,5.2.3 函数的调用、参数和返回值,运行情况如下: 1.5, 2.5 max is 2 函数max定义为整型,而return语句中的为实型,二者不
20、一致 ,按上述规定,先将转换为整型,然后max(x,y)带回一个 整型值2回主调函数main。如果将main函数中的c定义为实型,用 f格式符输出,也是输出2000000。 有时,可以利用这一特点进行类型转换,如在函数中进行实型运 算,希望返回的是整型量,可让系统去自动完成类型转换。但这 种做法往往使程序不清晰,可读性降低,容易弄错,而且并不是 所有的类型都能互相转换的(如实数与字符类型数据之间)。因 此建议初学者不要采用这种方法,而应做到使函数类型与return 返回值的类型一致。,5.2.3 函数的调用、参数和返回值,(4) 如果被调用函数中没有return语句,并不带回一个确定的、 用户
21、所希望得到的函数值,但实际上,函数并不是不带回值, 而只是不带回有用的值,带回的是一个不确定的值。例如,在 例5.1程序中,尽管没有要求p_star和p_message函数带回值,但 是如果在程序中出现下面的语句也是合法的: int a,b,c; ap_star(); bp_message(); cp_tstar(); printf(d,d,dn,a,b,c); 运行时除了得到和例5.1一样的结果外,还可以输出a、b、c的 值(今为38、15、38)。,5.2.3 函数的调用、参数和返回值,(5) 为了明确表示“不带回值”,可以用“void”定义“无类型”(或 称“空类型”)。例如,例5.1中
22、的定义可以改为 void printstar() void print-message() 这样,系统就保证不使函数带回任何值。如果已将p_star和 p_message函数定义为void类型,则下面的用法就是错误的: aprintstar(); bprint-message(); 编译时会给出出错信息。因为void类型的函数调用不能用在表 达式当中,只能在语句中单独使用. 为使程序减少出错,保证正确调用,凡不要求带回函数值的函 数,一般应定义为void类型。,5.2.3 函数的调用、参数和返回值,5.2.4 函数原型,调用一个函数之前,先要对其返回值类型、函数名和参数进行声明(declare
23、) 有助于编译器进行类型检查 声明时不要省略参数以及返回值的类型,用函数原型对被调用函数做说明 对被调用函数的要求: 必须是已存在的函数 库函数: #include 用户自定义函数: 函数类型说明 函数原型 一般形式: 函数类型 函数名(形参类型 形参名,. ); 或 函数类型 函数名(); 作用:告诉编译系统函数类型、参数个数及类型,以便检查。 函数定义与函数原型不同 函数说明位置:程序的数据说明部分(函数内或外) 下列情况下,可不作函数说明 若函数返值是char或int型,系统自动按int型处理 被调用函数定义出现在主调函数之前 有些系统(如Borland C+)要求函数说明指出函数返值类
24、型和形参类型,并且对void 和 int 型函数也要进行函数说明,5.2.4 函数原型,例 函数说明举例,5.2.4 函数原型,5.2.5 函数main()的特殊性,程序中只有一个main()函数 不管mian在什么位置,总是从main函数开始执行 定义main时,从来不指明其函数值类型和参数,也不用void ,实际上,它的返回值默认为int类型。 例5.4 不明确说明参数和返回值的函数 #include”stdio.h” func() printf(“in func()n”); main() func(1,2,3); 这样虽然合法,但是有太多的不确定性,所以在不需要函数 和返回值时,一般用v
25、oid标明。,5.3 变量的作用域和存储类型,5.3.1 变量的作用域 5.3.2 全局变量 5.3.3 变量的存储类型,5.3.1 变量的作用域,指在源程序中定义变量的位置及 其能被读写访问的范围 分为 局部变量(Local Variable) 全局变量(Global Variable ),例 int a; main( ) . . f2; . f1; . f1( ) auto int b; f2; . f2( ) static int c; ,5.3.2 局部变量与全局变量,局部变量-内部变量 定义:在函数内或语句块内定义,只在本函数或 语句块内有效 说明: main中定义的变量只在main
26、中有效 进入语句块时获得内存,仅能由语句块内语句访问, 退出语句块时释放内存,不再有效 定义时不会自动初始化,除非程序员指定初值 不同函数中同名变量,占不同内存单元 形参属于局部变量 可定义在复合语句中有效的变量,5.3.2 全局变量,全局变量-外部变量 定义:在函数外定义,可为本文件所有函数共用 有效范围:从定义变量的位置开始到本源文件结 束,及有extern说明的其它源文件,应尽量少使用全局变量,因为: 全局变量在程序全部执行过程中占用存储单元 降低了函数的通用性、可靠性,可移植性 降低程序清晰性,容易出错,定义 说明 次数: 只能1次 可说明多次 位置: 所有函数之外 函数内或函数外 分
27、配内存: 分配内存,可初始化 不分配内存,不可初始化,外部变量说明: extern 数据类型 变量表;,外部变量定义与外部变量说明不同,若外部变量与局部变量同名,则外部变量被屏蔽,float max,min; float average(float array, int n) int i; float sum=array0; max=min=array0; for(i=1;imax) max=arrayi; else if(arrayimin) min=arrayi; sum+=arrayi; return(sum/n); main() int i; float ave,score10; /*
28、Input */ ave=average(score,10); printf(max=%6.2fnmin=%6.2fn average=%6.2fn,max,min,ave); ,5.3.2 全局变量,extern char c1,c2;,5.3.2 全局变量,运行结果:max=13,extern int a,b; int max() int z; z=ab?a:b; return(z); main() printf(max=%d,max(); int a=13,b=-8;,5.3.2 全局变量,运行结果:max=8,概述 变量是对程序中数据的存储空间的抽象,编译或函数调用时为其分配内存单元,
29、10,程序中使用变量名对内存操作,5.3.3 变量的存储类型,数据在内存中的存储方式 静态存储:程序运行期间分配固定存储空间 动态存储:程序运行期间根据需要动态分配存储空间 内存用户区,生存期 静态变量:从程序开始执行到程序结束 动态变量:从包含该变量定义的函数开始执行至函数执 行结束,5.3.3 变量的存储类型,5.3.3 变量的存储类型,变量的属性 数据类型:变量所持有的数据的性质(操作属性) 存储属性 存储器类型:寄存器、静态存储区、动态存储区 生存期:变量在某一时刻存在-静态变量与动态变量 作用域:变量在某区域内有效-局部变量与全局变量 变量的存储类型 auto -自动型 regist
30、er-寄存器型 static -静态型 extern -外部型 变量定义格式: 存储类型 数据类型 变量表;,如: int sum; auto int a,b,c; register int i; static float x,y;,一 自动变量,函数中的局部变量,如不做专门的说明(说明为静 态的存储类别),都是动态分配存储空间的,存储在 动态存储区中。在调用该函数时系统会给它们分配存 储空间,在函数调用结束时就自动释放这些存储空 间。这类局部变量称为自动变量。自动变量用关键字 auto作存储类型的说明,“auto”也可以省略 。 例如: int f(int a)/*定义f函数,a为形参*/
31、auto int b,c3;/*定义b、c为自动变量*/ ,例 auto 变量的作用域,main() int x=1; void prt(void); int x=3; prt(); printf(“2nd x=%dn”,x); printf(“1st x=%dn”,x); void prt(void) int x=5; printf(“3th x=%dn”,x); ,运行结果: 3th x=5 2nd x=3 1st x=1,1、静态局部变量 有时希望函数中的局部变量的值在函数调用结束后不消失而保 留原值,即其占用的存储单元不释放,在下一次该函数调用 时,该变量已有值,就是上一次函数调用结束
32、时的值。这时就 应该指定该局部变量为“静态局部变量”,用关键字static进行 声明。通过下面简单的例子可以了解它的特点。 例8.17考察静态局部变量的值。 int f(int a) auto int b=0; static int c=3; b=b+1; c=c+1;,二 静态变量,return(a+b+c); #include void main() int i,a=2; for(i=0;i3;i+) printf(%d,f(a); ,二 静态变量,运行结果为: 789,对静态局部变量的说明: (1) 静态局部变量属于静态存储类别,在静态存储区内分配存 储单元。在程序整个运行期间都不释放。
33、 (2) 对静态局部变量是在编译时赋初值的,即只赋初值一次, 在程序运行时它已有初值。以后每次调用函数时不再重新赋初 值而只是保留上次函数调用结束时的值。而对自动变量赋初 值,不是在编译时进行的,而是在函数调用时进行,每调用一 次函数重新给一次初值,相当于执行一次赋值语句。 (3) 如在定义局部变量时不赋初值的话,则对静态局部变量来 说,编译时自动赋初值0(对数值型变量)或空字符(对字符 变量)。而对自动变量来说,如果不赋初值则它的值是一个不 确定的值。这是由于每次函数调用结束后存储单元已释放,下次 调用时又重新另分配存储单元,而所分配的单元中的值是不确定 的。,二 静态变量,(4) 虽然静态
34、局部变量在函数调用结束后仍然存在,但其他函 数是不能引用它的。 在什么情况下需要用局部静态变量呢? (1) 需要保留函数上一次调用结束时的值。 例如可以用下面方法求n!。 例打印1到5的阶乘值。 int fac(int n) static int f1; ff*n; return(f); main() int i; for(i1;i5;i) printf(d!dn,i,fac(i); ,二 静态变量,运行结果为: 1!1 2!2 3!6 4!24 5!120 (2) 如果初始化后,变量只被引用而不改变其值,则这时用 静态局部变量比较方便,以免每次调用时重新赋值。 但是应该看到,用静态存储要多占
35、内存(长期占用不释放, 而不能像动态存储那样一个存储单元可供多个变量使用,节 约内存),而且降低了程序的可读性,当调用次数多时往往 弄不清静态局部变量的当前值是什么。因此,如不必要,不 要多用静态局部变量。,二 静态变量,main() void increment(void); increment(); increment(); increment(); void increment(void) int x=0; x+; printf(“%dn”,x); ,例 局部静态变量值具有可继承性,运行结果:1 1 1,main() void increment(void); increment();
36、increment(); increment(); void increment(void) static int x=0; x+; printf(“%dn”,x); ,运行结果:1 2 3,、静态全局变量 在程序设计中希望某些外部变量只限于被本文件引用,而不能 被其他文件引用。这时可以在定义外部变量时加一个static声 明。例如: file1.cfile2.c static int A;extern int A; main ( )fun (int n) A=An; 在file1c中定义了一个全局变量A,但它用static声明,因 此只能用于本文件,虽然在file2c文件中用了extern
37、int ;但file2c文件中无法使用file1c中的全局变量A。这种 加上static声明、只能用于本文件的外部变量(全局变量)称 为静态外部变量。,二 静态变量,三 寄存器变量,一般情况下,变量(包括静态存储方式和动态存储方式)的值 是存放在内存中的。当程序中用到哪一个变量的值时,由控制 器发出指令将内存中该变量的值送到运算器中。 经过运算器进 行运算,如果需要存数,再从运算器将数据送到内存存放。,当对一个变量频繁读写时,必须要反复访问内存储器,从而花费 大量的存取时间。为此,语言提供了另一种变量,即寄存器变 量。这种变量存放在CPU的寄存器中,使用时,不需要访问内 存,而直接从寄存器中读
38、写,这样可提高效率。寄存器变量的说 明符是register。,只有函数内定义的变量或形参可以定义为寄存器变量。寄存 器变量只能是char、int和指针类型的变量。 一个计算机系统中的寄存器数目是有限的,不能定义任意多个 寄存器变量。,int fac(int n) register int i,f=1;/*定义寄存器变量*/ for(i=1;i main() int i; for(i=1;i=5;i+) printf(%d!=%dn,i,fac(i);,三 寄存器变量,从上可知,对一个数据的定义,需要指定两种属性:数据类型和 存储类别,分别使用两个关键字。 下面从不同角度做些归纳: (1) 从作
39、用域角度分,有局部变量和全局变量。 它们采用的存储类别如下: 局部变量 自动变量,即动态局部变量(离开函数,值就消失) 静态局部变量(离开函数,值仍保留) 寄存器变量(离开函数,值就消失) 形式参数可以定义为自动变量或寄存器变量 全局变量 静态外部变量(只限本文件引用) 外部变量(即非静态的外部变量,允许其他文件引用),四、存储类别小结,(2) 从变量存在的时间来区分,有动态存储和静态存储 两种类型。静态存储是程序整个运行时间都存在,而动 态存储则是在调用函数时临时分配单元。 自动变量(本函数内有效) 动态存储 寄存器变量(本函数内有效) 形式参数(本函数内有效) 静态局部变量(函数内有效)
40、静态存储 静态全局变量(本文件内有效) 非静态全局变量(其他文件可引用),四、存储类别小结,(3) 从变量值存放的位置来区分, 可分为: 内存中静态存储区: 静态局部变量 静态外部变量(函数外部静态变量) 外部变量(可为其他文件引用) 内存中动态存储区:自动变量和 形式参数 CPU中的寄存器:寄存器变量,图作用域的示意图,四、存储类别小结,(4) 关于作用域和生存期的概念。从前面叙述可以知道,对一 个变量的性质可以从两个方面分析,一是从变量的作用域,一 是从变量值存在时间的长短,即生存期。前者是从空间的角 度,后者是从时间的角度。二者有联系但不是同一回事。图 是作用域的示意图,图是生存期的示意
41、图。,图生存期的示意图,四、存储类别小结,5.4 函数的嵌套与递归调用,5.4.1 函数的嵌套调用 5.4.2 函数的递归调用,5.4.1 函数的嵌套调用,C语句不能嵌套定义函数,但可以嵌套调用函数,所谓函数 的嵌套调用,是指在执行被调用函数时,被调用函数又调用另 一个函数。见下图所示。,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,5.4.1 函数的嵌套调用,循序
42、渐进式编程实验4:小学生加法考试题,通过输入两个加数给学生出一道加法运算题,输入答案正确: right 错误: Not correct! Try again!,循序渐进式编程实验4:小学生加法考试题,void Print(int flag) if (flag) printf(Right!n); else printf(Not correct!n); ,实验4:小学生加法考试题,/* 函数功能: 计算两整型数之和,如果与用户输入的答案相同,则返回1,否则返回0 函数参数: 整型变量a和b,分别代表被加数和加数 函数返回值:当a加b的结果与用户输入的答案相同时,返回1,否则返回0 */ int A
43、ddTest(int a, int b) int answer; printf(%d+%d=, a, b); scanf(%d, ,main() int a, b, answer; printf(Input a,b:); scanf(%d,%d, ,do while (answer = 0);,实验4:小学生加法考试题,main() answer = AddTest(a, b); Print(answer); ,chance = 0; do chance+; while (answer = 0 ,实验4:小学生加法考试题,srand(time(NULL); error = 0; score =
44、 0; for (i=0; i10; i+) a = rand()%10 + 1; b = rand()%10 + 1; answer = AddTest(a, b); Print(answer); if (answer = 1) score = score + 10; else error+; ,实验4:小学生加法考试题,递归调用:一个函数直接或间接地调用了它本身,就称为函数的递归调用。 递归函数:在函数体内调用该函数本身。,int f(int x) int y,z; if( ) z=f(y); else return ; ,例如:,直接调用f 函数本身,5.4.2 函数的递归调用,在调用函
45、数f的过程中,又要调用f函数,这是直接调用本函数。 见下图a所示。,5.4.2 函数的递归调用,图 a 图b 在调用f1函数过程中要调用f2函数,而在调用f2函数过程中又 要调用f1函数,这是间接调用函数。见图b。 从图上可以看到,这两种递归调用都是无终止的自身调用。 显然,程序中不应出现这种无终止的递归调用,而只应出现 有限次数的、有终止的递归调用,这可以用if语句来控制,只 有在某一条件成立时才继续执行递归调用,否则就不再继续,【例】编一递归函数求n!。,思路:以求4的阶乘为例: 4!=4*3!,3!=3*2!,2!=2*1!,1!=1,0!=1。 递归结束条件:当n=1或n=0时,n!=
46、1。 递归公式:,5.4.2 函数的递归调用,程序如下:,float fact (int n) float f=0; if(n0) printf(n0,error!); else if (n=0 | n=1) f=1; else f=fact(n-1)*n; return (f); ,#include”stdio.h” void main( ) int n; float y; printf(nInput n:); scanf(%d, ,运行情况如下: Input a integer number:4 4!=24,5.5.2 函数的递归调用,5.5 预处理指令,编译预处理是指在系统对源程序进行编
47、译之前,对程序中 某些特殊的命令行的处理,预处理程序根据源代码中的预处 理命令修改程序。 预处理程序的位置在主函数之前,定义一次,可在程序中 多处展开和调用,它的取舍决定于实际程序的需要。预处理 程序一般包括:宏定义和宏替换、文件包含(又称头文 件)、条件编译。 预处理命令是一种特殊的命令,为了区别一般的语句,必 须以#开头,占单独书写行,结尾不加分号。预处理命令可以 放在程序中的任何位置,其有效范围是从定义开始到文件结 束。,5.5.1文件包含 5.5.2宏定义 5.5.3 条件编译,5.5.1 文件包含,功能:一个源文件可将另一个源文件的内容全部 包含进来。 文件包含命令行的一般形式为 i
48、nclude “包含文件名” 或 include 其中: (1)使用双引号:包含文件名中可以包含文件路 径,系统首先到当前目录下查找被包含文件,如果 没找到,再到系统指定的“包含文件目录”(由用户 在配置环境时设置)去查找。 (2)使用尖括号:直接到系统指定的“包含文件目 录”去查找。一般地说,使用双引号比较保险。,处理过程:预编译时,用被包含文件的内容取代该预 处理命令,再对“包含”后的文件作一个源文件编译。,5.5.1 文件包含,被包含文件内容 源文件(*.c) 头文件(*.h),宏定义 数据结构定义 函数说明等 不能有main函数,文件包含可嵌套,5.5.1 文件包含,例 文件包含举例,
49、5.5.1 文件包含,#define 标识符 字符串,宏定义的功能: 在进行编译前,用字符串原样替换程序中的宏名。 这个替换过程称为“宏替换”或“宏展开”,字符串 也称为替换文本。,1符号常量(不带参数)的宏定义 命令的一般格式:,5.5.2 宏定义,宏定义 命令,宏名,常数、表达 式、格式串,例如:,#define PI 3.14 main() float r,s,c; scanf(“%f”, ,替 换,3.14,3.14,5.5.2 宏定义,说明: (1)为了和变量名加以区别,宏名一般用大写字母表示。 (2)宏定义是用宏名替换一个字符串,不管该字符串的词法 和语法是否正确,也不管它的数据类
50、型,即不作任何检查。若 有错误,只能由编译程序在编译宏展开后的源程序时才发现。 (3)在宏定义时,可以使用已经定义的宏名。即宏定义可以 嵌套,可以层层替换。例如: #define R 3.0 #define PI 3.14159 #define L 2*PI*R #define S PI*R*R void main() printf(“L=%fnS=%fn”,L,S); ,5.5.2 宏定义,2带参数的宏定义 命令的一般形式,#define 宏名(形参表) 字符串,5.5.2 宏定义,(4)在程序中,用双引号括起来的宏名被认为是一般字符,并不 进行替换。例如: #define PAI 3.14
51、15 printf(“PAI*r*r=%f”,s); (5)宏定义是专门用于预处理命令的一个专用名词,它与定义变 量的含义不同,只作字符替换,不分配内存空间。,宏名和(之间 不能加空格,例1 #define S(a,b) a*b . area=S(3,2); 宏展开: area=3*2;,功能: 在编译预处理时,把源程序中所有带参数的宏名用宏定义中的字符串替换,并且用宏名后圆括号中的实参替换字符串中的形参。,5.5.2 宏定义,【例】带参数的宏定义。 #define MAX(x,y) (x)(y)?(x):(y) main( ) printf(%dn,a,b, MAX(a,b); printf(%dn,MAX(a+m,b+n); ,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五版航空航天样品采购与研发合同
- 二零二五年度豪华汽车买卖合同模板
- 二零二五版旅游车抵押租赁合作协议
- 二零二五年度比亚迪汽车购置升级服务合同
- 2025年高科技项目pc吊装劳务合作合同
- 二零二五年度茶叶种植基地租赁合同样本
- 2025届安徽省舒城一中物理高一下期末达标测试试题含解析
- 二零二五年仓储物流园区物业管理与安全评估合同
- 二零二五年度高档餐厅设备租赁及服务承包合同
- 二零二五年度茶叶礼品定制销售合同
- 重庆中考:数学高频考点
- IEC60335-1中文版本大全
- 厂房围墙承包协议书
- 熊猫旅居签署协议书
- 心衰药物治疗进展课件
- 化工仪表管理与维护
- 2024年“蓝桥杯”科学素养竞赛考试题库(含答案)
- 风力发电运维值班员(技师)职业技能鉴定考试题(附答案)
- 2025年贵州省水利投资集团有限责任公司招聘笔试参考题库附带答案详解
- 2025年上半年潜江市城市建设发展集团招聘工作人员【52人】易考易错模拟试题(共500题)试卷后附参考答案
- 广东省惠州市惠城区2024-2025 学年度第一学期期末教学质量检测八年级道德与法治试卷及答案(含答案)
评论
0/150
提交评论