语言-chap函数基本知识_第1页
语言-chap函数基本知识_第2页
语言-chap函数基本知识_第3页
语言-chap函数基本知识_第4页
语言-chap函数基本知识_第5页
已阅读5页,还剩32页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2015.07.21高级语言程序设计南京邮电大学先进技术研究院计算机软件教学中心邓松2015.07.21高级语言程序设计第05章函数的基本知识内容提要函数的定义、调用及原型声明

函数调用时实参向形参传递参数的方式不同存储类别变量的不同生命期与作用域简单的递归函数的定义及调用,理解其调用过程

35.1函数与模块化程序设计函数(function):是组成C程序的基本单位

函数分两类为什么自定义函数?--模块化程序设计的需要模块化程序设计方法:自顶向下、逐步分解、分而治之,即将一个大的程序按功能分解成为一些功能单一、结构清晰、接口简单、容易理解的功能模块。函数:模块化程序设计的最小单位、基石。4库函数:用户自定义函数:系统提供,需包含相应文件按需自行定义

例:一个简单的学生成绩档案管理系统问题描述:完成一个综合的学生成绩档案管理系统,要求能够管理若干个学生的几门课的成绩,需要实现以下功能:读入学生信息、以数据文件的形式存储学生信息;可以增加、修改、删除学生的信息;按学号、姓名、名次查询学生信息;可以依学号顺序浏览学生信息;可以统计每门课的最高分、最低分以及平均分;计算每个学生的总分并排名分成5大模块,各大模块又可以分几个小模块:显示基本信息基本信息管理学生成绩管理考试成绩统计根据条件查询按学号查按姓名查按名次查5.1函数与模块化程序设计函数定义的格式为:函数返回值类型函数名([形式参数表])

{ 一组语句}函数首部(也称函数头)需要提供3个方面的信息:函数返回值类型:函数最后需要传出的结果的类型函数名:一个合法的用户自定义标识符,见名知义形式参数表:列出所需要提供的参数及对应类型(不共享),代表使用这个函数必须提供的初始数据65.2

函数的定义函数首部,也称函数头函数体,可以为空每个形参之前必须指定形参类型;各形参之间以逗号分隔;可能无形参但括号不省做什么怎么做函数定义的首部举例:75.2

函数的定义函数的功能描述函数定义的首部求两个整数之间的较大值intFindMax(intx,inty)求两个实型数的和doubleAdd(doublea,doubleb)判断一个整数是否为质数intJudgePrime(intn)求出一个字符的ASCII码值返回intCalcuAscii(charc)求一个整数的阶乘doubleFact(intn)画出一条由30个减号组成的横线voidDrawLine()画出一条由n个减号组成的横线voidDrawLine(intn)画出一条由n个指定字符组成的横线voidDrawLine(intn,charc)求两个整数的最大公约数intGcd(inta,intb)统计出m到n之间的水仙花数intCountNarcissus(intm,intn)求出m到n之间所有的同时能被5和7整除的奇数和intSumOdd(intm,intn)函数体就是函数代码实现部分,其基本形式为:{ /*说明性语句*//*执行语句*/}若返回值类型不是void类型,则函数体内一定要配合使用“return表达式;”语句;若返回值类型为void,则在需要返回的点使用“return;”语句,建议编程者采用这种方式,也可以没有return语句,则执行到函数体的右大括号结束。85.2

函数的定义有可能函数体内无语句,空的函数体便于以后扩充函数体内不可以出现其他函数的定义,即函数不能嵌套定义例5.2

定义一个函数intJudgePrime(intn),实现判断任意一个正整数是否是质数。95.2

函数的定义/*函数功能:判断一个正整数是否为质数函数参数:一个整型形式参数函数返回值:int型,用值1表示n是质数,0表示不是质数*/intJudgePrime(intn){

inti,k;intjudge=1;if(n==1)

judge=0;

k=(int)sqrt(n);

for(i=2;judge&&i<=k;i++)if(n%i==0)

judge=0;

returnjudge;}?思考:当形式参数的值n获得值2或3时,能否得到正确的判断结果?例5.3定义一个函数DrawLine,实现画出一条由30个减号组成的横线105.2

函数的定义/*函数功能:画一条由30个减号组成的横线函数参数:无函数返回值:无*/voidDrawLine(){inti; for(i=1;i<=30;i++)printf("-"); printf("\n");return;} 现场练习改写1:函数voidDrawLine(intn),画出n个“-”号组成的直线

现场练习改写2:函数voidDrawLine(intn

,charc),画出n个由字符c组成的直线

调用一个已定义函数的基本形式为:函数调用JudgePrime函数求出所有的3位质数并按每行五个的形式输出名([实际参数表]);

例5.4

从键盘上读入一个整数m,如果m小于等于0,则给出相应的提示信息;如果m大于0,则调用JudgePrime函数判断它是不是一个质数,将结论在屏幕显示。115.3

函数的调用……if(JudgePrime(m)) printf("%disaprime!\n",m); else

printf("%disnotaprime!\n",m); …… 完整的代码在VC++下演示,此处只给出调用语句m为实参,是主函数中读入的变量,传值给形参n?深入思考:调用JudgePrime函数求出所有的3位质数并按每行五个的形式输出

例5.5

调用DrawLine函数实现划线功能。125.3

函数的调用

#include<stdio.h>/*此处省略例5.3中函数定义的代码*/ intmain(){ DrawLine();/*第一次调用DrawLine函数*/ printf("Cisabeautifullanguage!\n"); DrawLine();/*第二次调用DrawLine函数*/ return0;}函数DrawLine无形式参数

,因此不提供实参对于返回值类型为void型的函数,调用后直接作为函数调用语句使用函数调用的完整过程135.3

函数的调用(1)转向:调用函数时,流程从主调函数转向被调用函数(2)传参:如果调用有形参的函数,首先要用实际参数初始化形式参数;如果是无参函数,则不存在参数传递工作(3)执行:执行被调用函数的函数体直到返回处

(4)返回:执行到被调用函数的return语句处(如果无此语句,则是到函数体的右大括号处),执行流程返回到刚才主调用函数调用此函数处

(5)继续:继续执行主调函数后续语句至程序完请任课老师首先在VC++下用单步跟踪调试运行例5.4,观察函数调用的完整流程,注意参数函数可以嵌套调用实参(实际参数)与形参(形式参数)145.3

函数的调用参数性质出现位置本质表达的含义二者关系形式参数函数定义时在首部形式参数表中变量调用该函数的入口参数通用要求,需要几个、什么类型的值形式参数决定了实际参数的个数和对应类型,按从左到右的顺序一一匹配对应实际参数主调用函数中函数调用时提供表达式(常量、变量为其特殊形式)某一次特定调用时所提供的实际入口参数值(1)

形参在函数定义时不占内存空间,只有被调用时才占内存,且用实参的值初始化它。调用结束不再占用空间,等到下次被调用时重新再占用空间

(2)实参直接以表达式的形式给出,其个数与形参个数必须完全相同,对应数据类型最好完全一致返回值类型:指明了函数执行结束后结果的类型155.3

函数的调用(1)无名变量:当函数执行到return表达式

;

时,将自行定义无名变量接受return后表达式的值,该变量在被主调用函数使用过之后即消失(2)实参形式:表达式,其个数与形参个数必须完全相同,对应数据类型最好完全一致函数返回值类型函数体内是否有return语句函数执行到何处返回到调用点void型可以没有,但建议用return

;形式的语句如果没有return语句,则执行到函数体结束的右大括号返回调用处;如果有return语句,则执行到return语句处就返回调用处非void型必须有return表达式

;形式的语句执行到return表达式

;处就返回到调用处,无论后续是否还有其他的语句原型声明:函数应当先定义后调用,但如果出现了先调用后定义的情况,则必须在调用之前作原型声明原型声明形式:函数返回类型函数名([形式参数表]);几点说明:165.4

函数的原型声明(1)分号:原型声明最后的分号一定要有(2)形式参数表:形参变量名可以省略,形参类型一定要保留(3)声明位置:可以在程序的预处理指令之后第一个函数定义之前(建议这种),或者在主调用函数的调用点之前函数首部后加个分号例5.6定义一个函数Gcd用于求两个整数的最大公约数,该函数的定义在程序最后,函数需要原型声明。175.4

函数的原型声明#include<stdio.h>intgcd(intm,intn);intmain(){intm,n; scanf("%d%d",&m,&n);printf("gcd:%d\n",gcd(m,n)); return0;}intgcd(intm,intn){……}函数调用在先函数定义在后函数需要作原型声明intgcd(int,int);

可以省略形参变量名函数的递归:在定义函数时直接或间接地调用了自己直接递归:A--〉A(本章所讲)间接递归:A--〉B--〉A用递归求解问题的3个条件:原问题可转化为新问题,新旧问题的解决方法一样,但所处理的问题的规模越来越小可以应用这个转化过程使问题得到解决

必须要有一个明确的结束递归的终止条件

185.5

函数的递归很多问题适合用函数方法求解:阶乘问题:n!=乘幂问题:

xn=

数制转换问题汉诺塔问题(经典,请自己查阅、学习)例5.7:定义一个递归函数实现求n!主函数中读入任意一个整数,调用函数实现求阶乘。(注意与非递归函数作对比,重在理解递归的过程)195.5

函数的递归1(当n=0时)n*(n-1)!(当n>0时)1(当n=0时)x*xn-1(当n>0时)本例请在VC++下实际运行跟踪演示205.5

函数的递归递归函数代码:doubleFact(intn){ if(!n) return(1.0); return(n*Fact(n-1));}“递”即递推,表示将复杂的原问题转化为同类型同方法的简单问题的过程;“归”即回归,表示从递归调用终止处依次一层层向前返回处理结果。

非递归(迭达)函数代码:doubleFact(intn){ inti;doublef=1.0;for(i=1;i<=n;i++) f*=i;returnf;}每一次调用时形参n的值都不一样,用栈空间管理如果没有这个条件,会怎样?215.5

函数的递归例5.8数制转换:将十进制数n转化为B进制数算法步骤:重复执行以下步骤(1)和(2),直到n为0。 (1)利用取余运算n%B得到B进制数的一位,值的范围肯定是0到B-1。 (2)利用整除运算n=n/B将B进制数降一阶。 (3)从后往前输出每一次的余数,也就是说,第一次得到的余数最后一个输出,最后一次得到的余数最先输出。225.5

函数的递归voidMultiBase(intn,intB){intm; if(n) {MultiBase(n/B,B); m=n%B; if(m<10) printf("%d",m); else printf("%c",m+55); } }递归终止条件:n==0此处将输出语句放在递归调用后能实现逆序输出完整程序请在编程环境下演示并跟踪调试235.6

变量的作用域与存储类型

自定义函数引出了两个问题:第一,每一个变量在什么范围内起作用第二,每一个变量何时生成、何时消失,在程序中能存在多久。变量的作用域是指变量名应该在程序的哪一部分可以直接引用,即:在程序哪一部分可见并发挥作用变量的生命周期是指变量所占用的空间从创建到撤消的这段时间。二者的关系:一个变量如果不在其生命周期,肯定无作用域可言;如果在其生命周期,也未必一直起作用,即使起作用也是在特定的范围内。作用域问题生命周期问题245.6

变量的作用域与存储类型

变量的作用域取决于变量定义的位置位置有3种变量的生命周期取决于变量的存储类型,C语言中变量有4种不同的存储类型,分别用关键字auto、

static、extern、register表示。例5.9

3种不同位置定义的变量,具有不同的作用域函数体外函数体内函数语句块内外部(全局)变量,定义点到程序结束,但去掉同名局部量范围作用范围为本函数体内作用范围仅限于本语句块内在编程环境下演示,跟踪主要变量255.6

变量的作用域与存储类型#include<stdio.h>intcount;

intsumDigit(intn);intmain(){ inta,sum; scanf("%d",&a); if(a<0)a=-a; sum=sumDigit(a); printf("main:sum=%d,count=%d\n",sum,count);return0;}intsumDigit(intn){intsum=0,i; for(i=1;i<=5;i++){ intb; b=n%10; if(!b)break; sum+=b; n=n/10; count++; }printf("sumDigit:i=%d,count=%d,n=%d\n",i,count,n);returnsum;}265.6

变量的作用域与存储类型变量名变量定义位置变量性质变量的作用域特别说明count所有函数之外,程序的最开头全局(外部)变量从定义位置开始到程序结束,在下面的main和sumDigit函数中均有效编译后一直占用空间直至程序结束,其值变化按程序整个执行过程连续变化amain函数体开头局部变量整个的main函数体内作为调用函数的实际参数变量summain函数体开头局部变量整个的main函数体内与sumDigit函数中局部变量同名,但作用域不同,无冲突nsumDigit函数形参表中局部变量整个sumDigit函数体内其对应实参是main函数中的变量asumsumDigit函数体开头局部变量整个sumDigit函数体内与main函数中局部变量同名,但作用域不同,无冲突isumDigit函数体开头局部变量整个sumDigit函数体内用于控制循环,终止时最大为6bsumDigit函数体for循环语句块内局部变量只在for循环体内,不能在函数的其他位置访问语句块的一对括号类似于函数体的一对边界,作用域不出此界275.6

变量的作用域与存储类型

对例5.9作以下修改(只一处)运行观察现象:(1)在sumDigit函数体内增加一语句:printf("a=%d\n",a);(2)在sumDigit函数体return前增加语句:printf("b=%d\n",b);(3)删除sumDigit函数开头变量sum的定义(4)在sumDigit函数体内增加变量定义:

intcount=0;(5)将全局变量count的定义位置移到main函数后sumDigit之前285.6

变量的作用域与存储类型

变量的存储类型是指编译器为变量分配内存的方式,它决定变量的生存期变量用4种不同的关键字标识4种不同存储类型(1)auto:自动存储类型标识,为缺省存储类型(2)static:静态存储类型标识,表明该变量位于静态存储区(3)extern:外部存储类型标识,仅用于变量声明中,表示该变量在本文件的后面给出定义,或该变量来自于同一个程序的另一个文件中。(4)register:寄存器存储类型标识,表明该变量位于CPU的寄存器区域,加快了访问变量的速度。295.6

变量的作用域与存储类型

变量有存储类型和数据类型两个属性,因此,变量的完整定义格式是:[存储类型关键字]变量类型名变量名1[,变量名2,......变量名n];其中缺省的存储类型关键字为auto用auto定义自动变量函数内:自动局部变量,在程序运行进入函数体或语句块时自动获得内存空间,退出时不再占空间函数外:自动全局变量,编译时在静态存储区获得空间并自动初始化为0,程序结束时不再占空间生命期就是所在的函数或语句块被执行时

整个程序运行期间305.6

变量的作用域与存储类型

用static定义静态变量格式:static变量类型名变量名1[,变量名2,......变量名n];特点:编译时自动获得内存空间,直至程序运行结束空间才被回收,但是并非在生命期内的任何时刻都有作用域,有时呈“休眠”状态函数内:静态局部变量,在函数被调用时起作用,函数调用结束之后休眠至下一次调用时继续作用函数外:静态全局变量,仅在程序的当前文件中起作用,具体到第9章中介绍初始化只发生在第一次函数被调用时

315.6

变量的作用域与存储类型例5.10利用静态局部变量求解从1到5的阶乘#include<stdio.h>

intfun(intn){ staticintf=1;/*定义静态局部变量f*/f=f*n;/*求n的阶乘f*/ returnf;/*返回阶乘值*/}intmain(){inti;/*定义局部变量i*/ for(i=1;i<=5;i++)/*循环,依次求1到5的阶乘*/ printf(“%d!=%d\n",i,fun(i)); return0;}在编程环境下演示,跟踪主要变量325.6

变量的作用域与存储类型

静态局部变量的特点(1)静态局部变量在编译阶段就在静态存储区分配了存储空间,并且一直占用到程序结束。(2)静态局部变量其定义位置在函数内部,仍然是局部变量,所以其作用域仅限于本函数,仅在本函数被调用时才能被访问。(3)静态局部变量只在第一次进入函数时被初始化,若未指定初值,将自动初始化为0。(4)在第二次及以后进入函数时,静态局部变量不再初始化,而是从“休眠”状态“苏醒”,在原有基础上继续变化。每次退出函数时就进入“休眠”状态335.6

变量的作用域与存储类型

例5.10的思考题:

(1)如果将函数fun中的staticintf=1;改为intf=1;,重新运行程序,结果是?请解释原因;(2)如果将main函数中的循环for(i=1;i<=5;i++)改为for(i=3;i<=5;i++),f恢复成静态局部变量,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论