




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章函数与程序结构
本章要点1.C程序的一般结构,函数的定义方法及函数的类型和返回值。2.函数实参与形参的对应关系,以及参数传递的方式。3.函数的正确调用,嵌套与递归调用。4.局部变量和全局变量的概念和使用方法。5.变量的存储类别(自动、静态、寄存器、外部)、变量的作用域和生存期。6.宏定义及文件包含处理的方法。本章难点1.函数参数的传递。2.函数递归调用的执行过程。3.变量的作用域和生存期。5.1C程序的一般结构
5.1.1模块化程序设计自顶向下,逐步细化5.1.2C程序的一般结构函数是构成C语言程序的基本功能模块,是一段程序,它完成一项相对独立的任务。一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能。在c语言中,用函数来实现模块的功能。一个c程序由一个主函数和若干个函数组成,由主函数调用其他函数,其他函数之间也可以相互调用。同一个函数可以被一个或多个函数调用任意多次。特点:
①一个源文件程序由一个或多个函数以及其他有关内容(如命令行、数据定义等)组成。②一个c程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。③C程序的执行总是从主函数开始,又从主函数结束,其他函数只有通过调用关系发生作用。一个c程序有且仅有一个主函数main()。④所有的函数在定义时是相互独立的,一个函数并不从属于另一函数,即函数不能嵌套定义,不过函数之间可以相互调用,但不能调用main函数。⑤不同源文件的组装可以通过工程文件实现。函数的分类1.从用户使用的角度看,函数有两种:(1)标准函数,即库函数这是由系统提供的,用户不必自己定义这些函数,可以直接使用它们。应该说明,每个系统提供的库函数的数量和功能不同,当然有一些基本的函数是共同的。(2)用户自己定义的函数用以解决用户的专门需要。2.从函数的形式看,函数分两类:(1)无参函数在调用无参函数时,主调函数并不将数据传送给被调用函数,一般用来执行指定的一组操作。无参函数可以带回或不带回函数值,但一般以不带回函数值的居多。(2)有参函数在调用函数时,在主调函数和被调用函数之间有参数传递,也就是说,主调函数可以将数据传给被调用函数使用,被调用函数中的数据也可以带回来供主调函数使用。【例5.1】一个函数简单应用的例子。voidmain(){ voidp_star();/*说明p_star函数*/ voidp_message();/*说明p_message函数*/ p_star();/*调用p_staar函数*/ p_message();/*调用p_message函数*/ p_star();/*调用p_star函数*/}voidp_star()/*定义p_star函数*/{ printf("****************\n");}voidp_message()/*定义p_message函数*/{ printf("Goodmorning!\n");}5.2函数的定义与调用5.2.1函数的定义正如变量使用前应先定义一样,函数也应该先定义后使用。函数定义后,这个函数才存在,然后才能调用它。1.函数定义的一般形式(1)无参函数的定义其定义形式如下:类型标识符函数名(){说明部分语句部分}说明:其中的类型标识符用于指定函数值的类型;若函数无返回值,应用void说明。函数名的命名方法与标识符相同,不能和关键字、库函数名等同名函数名后的圆括号是函数的象征,不能省略。(在函数定义时,圆括号后无分号)例:voidp_star(){ printf("****************\n");}说明:其中的void在TC中可以省掉。p_message(){ printf("Goodmorning!\n");}(2)有参函数的定义其定义形式为:类型标识符函数名(形式参数说明表){
说明部分语句部分}说明:对于有参函数,函数的参数是主调函数和被调用函数的数据通道。参数可分为形式参数(形参)和实际参数(实参)两种。例如:intmax(intx,inty)/*形式参数说明*/{ intz;/*函数体中的说明部分*/ z=x>y?x:y;return(z);}main(){inta=3,b=4;printf(“%d”,max(a,b));}return语句的作用是将z的值作为函数值带回到主调函数中。return后面的括弧中的值作为函数带回的值(或称函数返回值)。在一个函数的函数体内,不能再定义另一个函数,即函数不能嵌套定义。
形参:定义函数时的参数实参:调用函数时的参数例.编写一个通用过程(函数或子程序),该过程可以实现判断一个整数是否为素数。编写主程序调用该过程,统计所有三位数中素数的个数。intss(intn){intflag,i;flag=1;for(i=2;i<=n-1;i++)if(n%i==0){ flag=0; break;}returnflag;}main(){intgs,i;gs=0;for(i=100;i<=999;i++)if(ss(i))gs++;printf("gs=%d",gs);}2.函数的返回值函数的数据类型就是函数返回值的类型,称为函数类型。(1)函数的返回值通过函数中的返回语句return将被调用函数中的一个确定的值带回到主调函数中去。return语句的用法如下:
return(表达式);
return表达式;
return;return语句的作用:使程序控制从被调用函数返回到主调函数中,同时把返回值带给主调函数;释放在函数的执行过程中分配的所有内存空间。【例5.2】在屏幕上显示计算结果时,有时会因为显示的速度太快,还没有看清楚结果,屏幕上的内容就已经滚出屏幕。为了解决这个问题,可以让屏幕每显示一定的行数后就自动暂停一个,待用户看清屏幕后按键盘上的任意键后。屏幕会继续显示以后的计算结果。#include<stdio.h>#include<string.h>/*显示数字1到100,每显示20行时暂停一次*/voidpause(void)/*函数定义,函数形式参数为空*/{getchar();}voidmain(){inti,j=0;for(i=1;i<=100;i++){printf("%d\n",i); if(++j==20) {j=0; pause(); }}}5.2.2函数的调用一般形式为:函数名(实参表列);说明:(1)调用函数时,函数名称必须与具有该功能的自定义函数名称完全一致。(2)实际参数表中的参数(简称实参),可以是常数、变量或表达式。如果实参不止1个,则相邻实参之间用逗号分隔。(3)实参的个数、类型和顺序,应该与被调用函数所要求的参数个数、类型和顺序一致,才能正确地进行数据传递。(4)对实参表求值的顺序并不是确定的,有的系统按自左至右顺序求实参的值,有的系统则按自右至左顺序。TurboC规定是自右至左顺序求值。main(){inti=2;printf(“%d%d%d”,++i,++i,++i);}
输出为:543为了避免这种影响,可以用:main(){inti=2,j,k,a;j=++i;k=++i;a=++i;printf(“%d%d%d”,j,k,a);}
按照函数在程序中出现的位置划分,调用函数方式有以下3种:(1)函数语句C语言中的函数可以只进行某些操作而不返回函数值,这时的函数调用作为一条独立的语句。如例8.1中的p_star();(2)函数表达式函数作为表达式的一项,出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。例如m=5*max(a,b);(3)函数实参函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。printf("%d",max(a,b));函数调用的执行过程main(){intn;scanf(“%d“,&n);if(ss(i))printf(“yes");elseprintf(“no");}intss(intn){intflag,i;flag=1;for(i=2;i<=n-1;i++)if(n%i==0){ flag=0; break;}returnflag;}12345其中的2称为函数调用;4称为函数返回。2.对被调用函数的说明和函数原型在调用自定义函数之前,应对该函数(称为被调用函数)进行说明,其—般格式如下:函数类型函数名(数据类型1[参数名1][,数据类型2[参数名2]…);C语言同时又规定,在以下两种情况下,可以省去对被调用函数的说明。(1)函数的返回值是整型或字符型,可以不必进行说明,系统对它们自动按整型说明。但为清晰起见,建议都加以说明为好。(2)被调用函数的函数定义出现在调用函数之前时。因为在调用之前,编译系统已经知道了被调用函数的函数类型、参数个数、类型和顺序。所以,养成这样的习惯:先定义子函数,最后写主函数。这样的话,可以省掉所有的函数说明。注意:①函数的“定义”和“说明”是两个不同的内容。“定义”是指对函数功能的确立,包括指定函数名,函数返回值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。在一个程序中,一个函数只能被定义一次,而且是在其他任何函数之外进行。而“说明”(有的书上也称为“声明”)则是把函数的名称、函数返回值的类型、参数的个数、类型和顺序通知编译系统,以便在调用该函数时系统对函数名称正确与否、参数的类型、数量及顺序是否一致等进行对照检查。在一个程序中,除上述可以缺省函数说明的情况外,所有调用函数都必须对被调用函数进行说明,而且是在调用函数的函数体内进行。②对库函数的调用不需要再作说明,但必须把该函数的头文件用#include命令包含在源文件前部。5.2.3函数的参数传递在调用函数时,大多数情况下,主调函数和被调用函数之间有数据传递关系。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。在c语言中,实参向形参传送数据的方式是“值传递”。值传递的优点就在于:被调用的函数不可能改变调用函数中变量的值,而只能改变它的局部的临时副本。这样就可以避免被调用函数的操作对调用函数中的变量可能产生的副作用。调用过程如下:(1)给形参分配内存空间;(2)将实参a的值传递给形参;(3)执行函数体。给函数体内的变量分配存储空间,执行算法实现部分函数返回的过程如下:①将返回值返回主函数,②释放函数调用过程中分配的所有内存空间,③结束函数调用,将流程控制权交给主调函数。举例说明:本讲义17页的例子。5.3局部变量与全局变变量的作用域:变量有效性的范围称变量的作用域。C语言中的变量,按作用域范围可分为两种,即局部变量和全局变量。5.3.1局部变量在一个函数或复合语句内定义的变量,称为局部变量,局部变量也称为内部变量。局部变量仅在定义它的函数或复合语句内有效。编译时,编译系统不为局部变量分配内存单元,而是在程序的运行中,当局部变量所在的函数被调用时,编译系统根据需要临时分配内存,函数调用结束,局部变量的空间被释放。对局部变量的说明:(1)主函数中定义的变量只能在主函数中使用,不能在其它函数中使用。(2)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。(3)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。例如,形参和实参的变量名都为n,是完全允许的。(4)在复合语句中也可定义变量,其作用域只在复合语句范围内。5.3.2全局变量全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序文件,可以被本文件中的所有函数共用。在函数中使用全局变量,一般应作全局变量说明。但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。对外部变量的说明:(1)外部变量定义必须在所有的函数之外,且只能定义一次。(2)设全局变量的作用是增加了函数间数据联系的渠道。由于同一文件中的所有函数都能引用全局变量的值,因此如果在一个函数中改变了全局变量的值,就能影响到其它函数,相当于各个函数间有直接的传递通道。intx,y;/*定义全局变量*/voidswap()/*定义函数swap()*/{intt;t=x;x=y;y=t;return;}voidmain(){printf(“inputx,y:”);scanf(“%d,%d”,&x,&y);swap(x,y);/*调用函数*/printf(“\noutputx,y:%d,%d\n”,x,y);}(3)虽然外部变量可加强函数模块之间的数据联系,但是又使函数要依赖这些变量,因而使得函数的独立性降低。从模块化程序设计的观点来看这是不利的,因此在不必要时尽量不要使用全局变量。(4)在同一源文件中,允许全局变量和局部变量同名。但在局部变量的作用域内,全局变量被“屏蔽”不起作用,即局部变量优先。5.4变量的存储类型1.自动变量2.静态变量
(1)静态局部变量
(2)静态全局变量3.外部变量4.寄存器变量5.5函数的嵌套与递归调用5.5.1函数的嵌套调用C语句不能嵌套定义函数,但可以嵌套调用函数,所谓函数的嵌套调用,是指在执行被调用函数时,被调用函数又调用另一个函数。
【例5.7】计算s=1k+2k+3k+……+Nk。#include<stdio.h>#defineK4#defineN5longf1(intn,intk){longp=n;inti;for(i=1;i<k;i++)p*=n;return(p);}longf2(intn,intk) {longsum=0;inti;for(i=1;i<=n;i++)sum+=f1(i,k);returnsum;}voidmain(){printf("%d\n",f2(N,K));}5.5.2函数的递归调用1.递归函数的概念递归是在连续执行某一个处理过程时,该过程中的某一步要用到它自身的上一步(或上几步)的结果。在一个程序中,若存在程序自己调用自己的现象就是构成了递归。递归又分为直接递归和间接递归。一个函数在它的函数体内直接或间接地调用它自身,称为递归调用。【例5.8】用递归法计算n!。floatff(intn){floatf;if(n<0)printf("n<0,inputerror");elseif(n==0||n==1)f=1;elsef=ff(n-1)*n;return(f);}voidmain(){intn;floaty;scanf("%d",&n);
y=ff(n);printf("%d!=%10.0f\n",n,y);}5.7编译预处理编译预处理是指在系统对源程序进行编译之前,对程序中某些特殊的命令行的处理,预处理程序将根据源代码中的预处理命令修改程序,使用预处理功能,可以改善程序的设计环境,提高程序的通用性、可读性、可修改性、可调试性、可移植性和方便性,易于模块化。预处理程序的位置在主函数之前,定义一次,可在程序中多处展开和调用,它的取舍决定于实际程序的需要。预处理程序一般包括:宏定义和宏替换、文件包含(又称头文件)、条件编译。预处理命令是一种特殊的命令,为了区别一般的语句,必须以#开头,结尾不加分号。预处理命令可以放在程序中的任何位置,其有效范围是从定义开始到文件结束。5.7.1宏定义宏定义可以分为符号常量(不带参数)和带参数的两种。也可以使用#undef命令终止宏定义的作用域。1.符号常量(不带参数)的宏定义用一个指定的标识符(即名字)来代表一个字符串,其一般形式为:#define标识符字符串宏定义的功能:在进行编译前,用字符串原样替换程序中的标识符。宏定义的作用:(1)便于对程序进行修改。(2)提高源程序的可移植性。(3)减少源程序中重复书写字符串的工作量。【例5.10】输入圆的半径,求圆的周长、面积和球的体积。要求使用无参宏定义圆周率。#include<stdio.h>#definePI3.1415926 /*PI是宏名,3.1415926用来替换宏名的常数*/voidmain(){doubleradius,length,area,volume;printf("Inputaradius:");scanf("%lf",&radius);length=2*PI*radius; /*引用无参宏求周长*/area=PI*radius*radius; /*引用无参宏求面积*/volume=PI*radius*radius*radius*3/4; /*引用无参宏求体积*/printf("length=%.2lf,area=%.2lf,volume=%.2lf\n",length,area,volume);}对宏的说明:(1)为了和变量名加以区别,宏名一般用大写字母表示(2)宏定义是用宏名替换一个字符串,不管该字符串的词法和语法是否正确,也不管它的数据类型,即不作任何检查。如果有错误,只能由编译程序在编译宏展开后的源程序时发现。(3)在宏定义时,可以使用已经定义的宏名。即宏定义可以嵌套,可以层层替换。例如#defineR3.0#definePI3.14159#defineL2*PI*R#defineSPI*R*Rvoidmain(){printf(“L=%f\nS=%f\n”,L,S);}替换为printf(“L=%f\nS=%f\n”,2*PI*R,PI*R*R);printf(“L=%f\nS=%f\n”,2*3.14159*3.0,3.14159*3.0*3.0);(4)在程序中,用双引号括起来的宏名被认为是一般字符,并不进行替换。#definePAI3.1415printf(“PAI*r*r=%f”,s);/*并不用3.1415替换PAI*/2.带参数的宏定义对带参数的宏,在调用中,不是进行简单的字符串替换,还要进行参数替换。带参宏定义的一般形式为#define宏名(形参表)字符串带参宏调用的一般形式为宏名(实参表);例如,#defineM(y)((y)*(y)+3*(y))/*宏定义*/k=M(5);/*宏调用*/在宏调用时,用实参5去替换形参y,经预处理宏展开后的语句为:k=((5)*(5)+3*(5));其中的括号是必要的。(1)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。#defineSQ(y)(y)*(y)voidmain(){inta,sq;printf("inputanumber:\n");scanf("%d",&a);sq=SQ(a+1);printf("sq=%d\n",sq);}(2)在带参宏中,只是符号替换,不存在值传递的问题。(3)在定义有参宏时,在所有形参外和整个字符串外,均应加一对圆括号。例如:求10/(3*3)#defineSQ(x)x*x /*宏定义*/printf(“%f\n”,10/SQ(3));/*宏调用*/替换后printf(“%f\n”,10/3*3);显然这是一个错误的结果。宏定义应改为#defineSQ(x)(x*x)替换后printf(“%f\n”,10/(3*3));(4)定义带参宏时,宏名与左圆括号之间不能留有空格。否则,C编译系统将空格以后的所有字符均作为替换字符串,而将该宏视为无参宏。(5)带参的宏和带参函数很相似,但有本质上的不同。(6)宏定义也可用来定义多个语句,在宏调用时,把这些语句又替换到源程序内。例如,#defineSSSV(s1,s2,s3,v)s1=l*w;s2=l*h;s3
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度康复护理聘用护工服务合同
- 二零二五年度影视制作员工劳务派遣合作协议
- 2025年度现代农业用地租赁与智能温室厂房建设合同
- 二零二五年度酒吧特色餐饮服务房屋租赁及营销合同
- 2025年度生态住宅区物业管理移交协议书
- 2025年度食品行业增值税专用发票转让及质量保证协议
- 水力资源开发对环境保护的影响研究
- 知识产权保护在互联网时代的创新教育模式
- 沟通中的语言艺术与文化敏感度
- 实习生教育调查报告集合6篇
- 新媒体艺术设计交互融合元宇宙
- 家长会课件:七年级家长会优质课件
- 《AutoCAD 中文版实例教程(AutoCAD 2020) (微课版)(第 2 版)》课件 马连志 第1、2章 AutoCAD 2020 中文版入门、绘图设置
- 小学一年级硬笔书法入门班教学课件
- 出货单表格模板下载
- PEP六年级上册英语unit1
- 接纳与承诺(ACT)疗法课件
- 装配式混凝土建筑技术标准
- 房地产公司销售管理部赏罚制度
- 《方位介词介绍》课件
- 甲状腺术后出血抢救流程
评论
0/150
提交评论