欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > PPT文档下载  

    第6章函数与预处理.ppt

    • 资源ID:4722623       资源大小:526.52KB        全文页数:102页
    • 资源格式: PPT        下载积分:10金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要10金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    第6章函数与预处理.ppt

    2023/5/11,第6章 函数与预处理,教学目的通过本章的学习,要求能熟练掌握函数的定义和调用方法,掌握函数的嵌套调用和递归调用,理解变量的作用域和存储类别,掌握内部函数和外部函数,并能够在不同情况下灵活选择函数来解决实际问题。掌握编译预处理命令的使用方法。函数和预处理命令是编写模块化程序的重要方法,这将为编写比较复杂的程序设计的学习打下基础。,2023/5/11,第6章 函数与预处理,教学内容,函数,编译预处理,文件包含命令,宏定义,条件编译,函数调用,变量的作用域和存储类别,内部函数和外部函数,函数的定义,2023/5/11,第6章 函数与预处理,重点难点 重点:(1)函数的定义和函数调用(2)函数的参数传递 难点(1)函数的参数传递(2)递归调用(3)变量的作用域和存储特性,2023/5/11,6.1 函数应用实例,程序员在设计一个复杂的应用程序时,往往将整个程序划分为若干个功能较为单一的程序模块,然后分别予以实现,最后将所有的程序模块像积木一样装配起来,这种在程序设计中逐步分解、分而治之的的方法,称之为模块化程序设计。在C语言中,一个函数实现一个特定的功能。一个C语言程序可以由一个主函数和若干个其它函数构成,由主函数调用其它函数,其它函数也可以相互调用。同一个函数可以被一个函数或多个函数调用任意多次。因此,在设计时,往往将一些常用的功能模块编写成为函数,放在函数库中,供大家选用或多次调用,以减少重复性的编写程序。程序员可以方便地利用函数作为程序模块,来实现 C语言程序设计的模块化。,6.1.1 模块化设计,2023/5/11,在第1章中已经介绍过,源程序是由函数组成的。虽然在前面各章的程序中都只有一个主函数main(),但实用程序往往由多个函数组成。函数是源程序的基本模块,通过对函数模块的调用实现特定的功能。语言中的函数相当于其它高级语言的子程序。语言不仅提供了极为丰富的库函数(如Turbo C,MS C 都提供了三百多个库函数),还允许用户建立自己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。可以说程序的全部工作都是由各式各样的函数完成的,所以也把语言称为函数式语言。由于采用了函数模块式的结构,语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。,6.1.2 函数的基本概念,2023/5/11,先举两个函数调用的简单例子。(该例子引自书本配套案例“学生成绩管理系统”)【例6.1】函数调用的简单例子输入如下代码:/*源文件名:Li6_1.c功能:在屏幕输出显示一个菜单*/#include stdio.hvoid main()void printstart();/*对printstart()函数进行声明*/void menu();/*对 menu()函数进行声明*/printstart();/*调用printstart()函数*/menu();/*调用 menu()函数*/printstart();/*调用printstart()函数*/,6.1.3 函数的引入实例,2023/5/11,void menu()printf(*n);printf(t1登记学生成绩t tt2删除学生信息n);printf(t3查询学生信息t tt4修改学生资料n);printf(t5保存学生信息t tt6学生成绩排序n);printf(t7统计学生成绩t tt8输出学生信息n);printf(t0退出系统n);printf(*n);void printstart()printf(-n);,6.1.3 函数的引入实例,2023/5/11,编译、连接、运行程序。程序运行后,屏幕显示:printstart()和menu()都是用户定义的函数,分别用来输出一行下划线和一个菜单信息。在定义这两个函数的函数时,前面加上void,表示该函数无类型,也就是函数没有返回值。而函数名后面的括号里面是空的,说明该函数没有参数。,6.1.3 函数的引入实例,2023/5/11,【例6.2】函数调用的简单例子/*源文件名:Li6_2.c功能:求两个整数的和*/#include stdio.hvoid main()int sum(int x,int y);/*对sum()函数进行声明*/int a,b,s;scanf(%d%d,6.1.3 函数的引入实例,2023/5/11,编译、连接、运行程序。程序运行后,屏幕显示:程序中sum(int x,int y)函数带有两个整型参数x和y,表示sum是个有参函数,而在函数名前面加上int,表示该函数会返回一个整型的数值。,6.1.3 函数的引入实例,2023/5/11,说明:(1)源程序是由函数组成的。函数是源程序的基本模块,通过对函数模块的调用实现特定的功能。语言中的函数相当于其它高级语言的子程序。语言不仅提供了极为丰富的库函数,还允许用户建立自己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。可以说程序的全部工作都是由各式各样的函数完成的,所以也把语言称为函数式语言。由于采用了函数模块式的结构,语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。(2)一个程序总是从main()函数开始执行,调用其他函数后,流程回到main主函数结束。main主函数是系统定义的,必须有且只能有一个名为main的主函数。(3)所有函数都是平行的,它们的定义都是相互独立的。一个函数并不从属于另外一个函数,即函数不能嵌套定义。函数间可以互相调用,即函数可以嵌套调用。但不能调用main函数,main函数是系统调用的。,6.1.3 函数的引入实例,2023/5/11,从函数使用的角度看,函数可分为库函数和用户定义函数两种。(1)库函数:由编译系统提供的已设计好的函数,用户只需调用而无须实现它,在编译 C程序时,应尽可能的使用库函数。这样可以提高编程效率和编程的质量。在前面各章的例题中反复用到的printf、scanf、getchar、putchar、gets、puts、strcat等函数均属于库函数。使用库函数时应注意:1)函数的功能;2)函数参数的数目、顺序以及每个参数的意义和类型;3)函数返回值的意义和类型;4)需要使用的包含文件。要调用某个库函数,则需在程序的头部用包含命令(#include)将说明该函数原型的头文件包含进本程序中。,6.1.4 函数的分类,2023/5/11,(2)用户定义函数:顾名思义,就是程序员自行定义和设计的函数。库函数一般只能提供一些低层服务的功能。而用户自定义的函数则能针对具体的应用实现一些特殊的功能。用自定义函数需要程序员自己来编写函数功能的实现代码。用户自定义函数是由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。,6.1.4 函数的分类,2023/5/11,语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。(1)有返回值函数:此类函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。如数学函数即属于此类函数。由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明中明确返回值的类型。(2)无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此类函数时可指定它的返回为“空类型”,空类型的说明符为“void”。,6.1.4 函数的分类,2023/5/11,6.2 函数的定义,定义无参函数的一般形式为:【函数类型】函数名()声明语句部分可执行语句部分,6.2.1无参函数定义的一般形式,2023/5/11,6.2 函数的定义,例6.1中的printstart()和menu()函数都是无参函数。在定义函数时要用类型标识符来指定函数类型,即函数返回值的类型。如在例6.1中的printstart()和menu()函数的函数类型都为void,表示不需要带回函数值。而例6.2中的sum()函数的函数类型为int,表示该函数会返回一个整型数值。注意:在函数类型缺省的情况下,系统一律按int型处理。,6.2.1无参函数定义的一般形式,2023/5/11,定义有参函数的一般形式为:【函数类型】函数名(数据类型 参数【,数据类型 参数2】)声明语句部分可执行语句部分,6.2.2 有参函数定义的一般形式,2023/5/11,例6.2中的sum(int x,int y)就是一个有参函数。详细分析一下sum函数:int sum(int x,int y)/*对函数sum()进行定义*/int z;/*函数体中的声明部分*/z=x+y;return z;这是一个求两个整型数据之和的函数。第一行中的int 表示函数返回值是整型的,sum为函数名,括号中的两个形式参数x和y都是整型的。在调用该函数的时候,主调函数把实际参数的值传递给被调用函数中的形式参数x和y。大括号内是函数体,通过函数体中语句的执行求出x和y的和,通过return z;语句把z作为函数值返回到主调函数中。return后面的z也称为函数返回值。注意:一般情况下,函数返回值的类型和函数类型是一致的。如果不一致,则以函数类型为准。,6.2.2 有参函数定义的一般形式,2023/5/11,一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能。而在第一个阶段只设计最基本的模块,功能可以在设计好框架以后慢慢补上。在编写程序的开始阶段,往往是先写上一个空函数,占个位置,以后再用编写好的函数来替换它。这样,程序的结构比较清楚,可读性比较好,以后扩充新功能也比较方便,对程序结构影响不大。在程序设计中空函数常常被使用到,它的定义形式为:【函数类型】函数名(),6.2.3 空函数,2023/5/11,例如,在设计一个学生成绩管理系统的开始阶段,可以运用空函数设计如下的程序框架 void main()void addscore()/*addscore()函数实现成绩的添加*/void alterscore()/*alterscore()函数实现成绩的修改*/void deletescore()/*deletescore()函数实现成绩的删除*/之后编写程序的时候,在设计好的框架上完善就可以了。,6.2.3 空函数,2023/5/11,6.3 函数的参数和返回值,函数的参数分为形参和实参两种。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。,6.3.1 形式参数和实际参数,2023/5/11,6.3 函数的参数和返回值,函数的形参和实参具有以下特点:1.形参变量只有在被调用时才分配内存单元,在调用结束时,所分配的内存单元也将被释放。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值、输入等办法使实参获得确定值。3.实参和形参在数量、类型、顺序上应严格一致,否则会发生类型不匹配的错误。4.函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。,6.3.1 形式参数和实际参数,2023/5/11,函数的返回值是指函数被调用之后,执行函数体中的程序段所取得的并通过return语句返回给主调函数的值。如调用sqrt()函数取得开根号的值,调用例6.2的sum函数取得的两数之和等。对函数的返回值(或称函数的值)有以下一些说明:1.函数的值只能通过return语句返回主调函数。return 语句的一般形式为:return 表达式;或者为:return(表达式);该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个return语句,但每次调用只能有一个return 语句被执行,因此一个函数最多只能返回一个函数值。例如:sign(int x)if(x0)return(1);if(x=0)return(0);if(x 0)renturn(-1);,6.3.2 函数的返回值,2023/5/11,2.函数返回值的类型和函数定义中的函数类型应保持一致。如果两者不一致,则以函数类型为准,自动进行类型转换。3.如果函数返回值为整型,在函数定义时可以省去类型说明。4.没有返回值的函数,可以明确定义为“空类型”,类型说明符为“void”。如例6.1中函数printstart()并不向主函数返回值,因此可定义为:void printstart()一旦函数被定义为空类型后,就不能在主调函数中使用被调函数的函数值了。例如,在定义printstart为空类型后,如果在主函数中写下述语句 sum=printstart();就是错误的。为了使程序有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型。,6.3.2 函数的返回值,2023/5/11,使用函数定义时的几点说明:1)最好在参数列表中列出每个参数的类型,即使参数是默认的int。2)如果参数的类型声明放在()内的形参表中,则对每个形参都要进行对应的类型说明,不能省略。如:float max(float x,float y)不能写成float max(foat x,y)。此时y为系统缺省的int类型。3)无论有无形参,函数名后的括号都不能省略。4)函数不能嵌套定义,既一个函数的定义不能从属于另外一个函数。5)应尽可能多的使用系统提供的库函数。6)为避免混淆,传递给函数的参数和函数定义中的相应的参数尽量不使用相同的名字。7)选择有意义的参数名和函数名可以使程序具有良好的可读性,这可以避免过多地使用注释。8)需要大量参数的函数可能包含较多的功能(任务),这种情况应该考虑将该函数分成完成单个任务的较小的函数。9)函数原型、函数头部和函数调用应该具有一致的参数个数、参数类型、参数顺序和返回值类型。,6.3.2 函数的返回值,2023/5/11,6.4 函数调用,函数调用的一般形式为:函数名(实参列表);如果调用无参函数,则”实参列表”是空的,但括号不能省略,见例6.1。如果实参列表包含多个实参,则各参数间用逗号隔开。实参和形参的个数应相等,类型应匹配。实参与形参按顺序对应,一一传递数据。当实参列表包括多个值时,对实参求值的顺序并不确定。有的系统按自左向右求实参的值,有的则按自右向左的顺序。要注意的是,visual c+6.0编译器是按自右向左的顺序求实参列表的值的。,6.4.1 函数调用的一般形式,2023/5/11,6.4 函数调用,【例6.3】测试多个实参的求值顺序(从右向左)/*源文件名:Li6_3.c功能:测试多个实参的求值顺序*/#include stdio.hvoid main()/int compare(int a,int b);/*函数声明*/int i=2,p;p=compare(i,+i);/*函数调用*/printf(%dn,p);int compare(int a,int b)/*函数定义*/int c;if(ab)c=1;else if(a=b)c=0;else c=-1;return c;,6.4.1 函数调用的一般形式,2023/5/11,6.4 函数调用,编译、连接、运行程序。程序运行后,屏幕显示:如果按自左向右顺序求实参的值,则函数调用相当于compare(2,3),程序运行的结果应为”-1”。若按自右向左顺序求实参的值,则相当于compare(3,3),程序运行结果为”0”。如果不清楚自己所用的编译器对实参的求值顺序,用上述代码上机一试就清楚了。注意:由于不同的编译器对实参的求值顺序不一样,为了使程序的通用性不受影响以及避免大家对同一段代码产生不同的理解,应尽量避免使用这种容易混淆的用法。,6.4.1 函数调用的一般形式,2023/5/11,按函数在程序中出现的位置来分,可以有一下三种函数调用的方式。1.函数语句把函数调用作为一个语句。如例6.1中的printstart();这时不要求函数带返回值,只要求函数完成一些操作。2.函数表达式函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参加表达式的运算。例如:s=sum(a,b)+sum(x,y);函数sum是表达式的一部分,将sum(a,b)的值加上sum(x,y)的和赋值给s。3.函数参数函数调用作为一个函数的参数,例如:s=sum(a,sum(b,c);其中,sum(b,c)是一次函数调用,它的值作为sum另一次调用的参数。s的值为a,b,c三数的总和。其实,函数调用作为函数的参数,也是函数表达式调用的一种形式,因为函数参数本身就是一个表达式的形式。,6.4.2 函数调用的方式,2023/5/11,如果一个函数要调用另外一个函数,首先是被调用的函数必须存在。其次还应在主调函数中对所有被调函数加以说明,否则,在连接时会出现找不到所调用函数的错误信息。同变量一样,函数的调用也应遵循“先定义后使用”的原则。对被调函数的声明分为两种情况:(1)如果被调函数是C语言系统提供的标准库函数,则在源程序文件的开头处,使用#include命令,将存放所调用库函数的有关“头文件”包含到该程序文件中来。#include命令的一般形式为:#include或#include stdio.h,6.4.3 对被调用函数的声明和函数原型,2023/5/11,(2)如果被调用函数为用户自己定义的函数,一般情况下,应在主调函数中对被调用函数(返回值)的类型进行说明。函数的说明方法是:在主调函数的声明部分对被调函数进行声明。在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。其一般形式为:类型说明符 被调函数名(类型 形参,类型 形参);或者:类型说明符 被调函数名(类型,类型);括号内给出了形参的类型和形参名,或只给出形参类型。这便于编译系统进行检错,以防止可能出现的错误。,6.4.3 对被调用函数的声明和函数原型,2023/5/11,例6.1 main函数对printstart()函数的说明为:void printstart();例6.2 main函数对sum()函数的说明为:int sum(int x,int y);也可以写成:int sum(int,int);,6.4.3 对被调用函数的声明和函数原型,2023/5/11,语言中规定在以下几种情况时可以省去在主调函数中对被调函数的函数说明。1)当被调函数的返回值是整型或字符型时,可以不对被调函数作说明。这时系统会自动对被调函数返回值按整型处理。例6.3的主函数中把函数声明语句int compare(int a,int b);注释掉而直接调用就属于这种情况。2)当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数再作说明而直接调用。例如例6.1中,函数printstart()的定义放在 menu()函数之前,因此可在 menu()函数中省去对printstart()函数的函数说明void printstart();3)如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调函数中,可以不再对被调函数作说明。,6.4.3 对被调用函数的声明和函数原型,2023/5/11,例如:long factor(int a);long sum(int b);void main()long factor(int a)long sum(int b)其中第一、二行对factor函数和sum函数预先作了说明。因此在以后各函数中无须对factor和sum函数再作说明就可直接调用。,6.4.3 对被调用函数的声明和函数原型,2023/5/11,6.5 函数的嵌套调用和递归调用,【例6.4】计算=1!+2!+n!(n1,20的整数,从键盘输入)。算法设计要点:本案例可以设计2个函数:factor()用于求n!;sum()通过调用factor()来实现求。输入如下代码:/*源文件名:Li6_4.c功能:求阶乘和*/#include stdio.hlong factor(int n)/*定义求阶乘函数factor()*/int i;long f=1;for(i=1;i=n;i+)f=f*i;return f;,6.5.1 函数的嵌套调用实例,2023/5/11,6.5 函数的嵌套调用和递归调用,long sum(int m)/*定义求和函数sum()*/int i;long s=0;for(i=1;i=m;i+)s+=factor(i);/*调用factor()函数*/return s;void main()int n;long s;printf(please input a number:);scanf(%d,6.5.1 函数的嵌套调用实例,2023/5/11,6.5 函数的嵌套调用和递归调用,编译、连接、运行程序。程序运行后,屏幕显示:,6.5.1 函数的嵌套调用实例,2023/5/11,6.5 函数的嵌套调用和递归调用,在该案例中,主函数main()调用求和函数sum(),sum()又调用求阶乘函数factor()。其调用关系图如下:,6.5.1 函数的嵌套调用实例,2023/5/11,一般地说,函数的嵌套调用是指在执行被调用函数时,该函数又调用其它函数的情形。语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是语言允许在一个函数的定义中出现对另一个函数的调用,这样就出现了函数的嵌套调用,即在被调函数中又调用其它函数。这与其它语言的子程序嵌套的情形是类似的。注意:被调用函数执行完毕后,将返回到调用函数的断点继续执行。简言之,谁调用返回到谁的断点继续执行。,6.5.2函数的嵌套调用说明,2023/5/11,【例6.5】用递归方法计算n!(1!=1,n!=(n-1)!*n(n2))算法分析:(1)根据计算n!的递归定义可知,为了计算n!,必须首先计算(n-1)!;依此类推,直至1!(1!=1)。(2)依据1!求2!=1!*2,再依据2!求3!=2!*3;同理,依据(n-1)!求n!=(n-1)!*n。输入如下代码:/*源文件名:Li6_5.c功能:利用递归求一个数的阶乘*/,6.5.3函数的递归调用实例,2023/5/11,#include stdio.hlong factor(int n)/*定义求阶乘函数factor()*/int i;long f;if(n1)f=n*factor(n-1);else f=1;return f;void main()int n;long s;printf(please input a number:);scanf(%d,6.5.3函数的递归调用实例,2023/5/11,编译、连接、运行程序。程序运行后,屏幕显示:其中,factor函数在定义的过程中调用了本身,这种情况叫做函数的递归调用。,6.5.3函数的递归调用实例,2023/5/11,一个函数在它的函数体内调用它自身称为递归调用。这种函数称为递归函数。语言允许函数的递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层。下面以例6.5说明一下递归的执行过程。设执行本程序时输入为5,即求5!。在主函数中的调用语句即为s=factor(5);,进入factor函数后,由于n=5,大于1,故应执行f=n*factor(n*1),即f=factor(5-1)*5。该语句对factor函数作递归调用即factor(4)。进行四次递归调用后,factor函数形参取得的值变为1,故不再继续递归调用而开始逐层返回主调函数。factor(1)的函数返回值为1,factor(2)的返回值为2*1=2,factor(3)的返回值为3*2=6,factor(4)的返回值为4*6=24,最后返回值factor(5)为5*24=120。注意:为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的方法是加条件对递归是否继续进行判断,满足某种条件后就不再作递归调用,而是逐层返回。如例6.5中的条件if(n1)就是控制递归继续的条件,当n不小于1的时候递归就终止,开始回朔的过程。,6.5.4函数的递归调用说明,2023/5/11,6.6 内部函数和外部函数,一个函数如果只能被本文件中其它函数所调用,称为内部函数,内部函数又称静态函数。在定义内部函数时在函数名和函数类型前面加static。即:static 函数类型 函数名(形参列表)函数体 例:static int fun(int a,int b)特点:只能被本文件中的函数所调用。优点:不用担心与其它源文件中的函数同名,因为即使同名也没关系。,6.6.1 内部函数,2023/5/11,一个函数可以被其它文件中其它函数所调用,就称为外部函数。在定义函数时可冠以关键字extern(省略也可),表示此函数是外部函数。即:extern 函数类型 函数名(形参列表)函数体如:extern int fun(int a,int b)特点:允许被所有源文件中的函数所调用。注意:调用其它源文件中的外部函数时,需要对其进行说明。,6.6.2 外部函数,2023/5/11,【例6.6】以多文件的形式实现加、减、乘、除和求余数运算程序 说明:将实现加、减、乘、除和求余数运算的程序段分别作为1个独立的函数、存储在1个独立的源文件中。程序框架如下(完整程序详见【例6.6】源代码):分别创建addition.c源文件、subtraction.c源文件、multiplication.c源文件、division.c源文件、remainder.c源文件,分别在各源文件中实现相应的加、减、乘、除和求余数函数的功能。,6.6.2 外部函数,2023/5/11,编译、连接、运行程序。程序运行后,屏幕显示:,6.6.2 外部函数,2023/5/11,在软件工程项目中,采用结构化方法进行程序设计与编程,通常会产生多个源文件(例如源程序文件、数据结构定义文件等)。那么,如何将这些源文件编译、连接成一个统一的可执行文件呢?一般有两种方法:1.分别编译、一并连接 C编译程序是以源文件为编译单位。当一个程序中的函数和数据结构分放在多个源文件中时,先将各文件分别编译,再通过link命令产生一个可执行文件(.exe)。2.集中编译、连接 利用编译预处理命令#include,将其它源文件包含到主函数main()所在的源文件的开头,然后直接编译该文件即可。,6.6.3 多个源文件的编译与连接,2023/5/11,【例6.7】先通过添加源文件的操作将addition.c源文件,subtraction.c源文件,multiplication.c源文件,division.c源文件,remainder.c源文件添加到本工程(加减乘除求余)来,然后在其主函数main()里添加如下几行:/*源文件名:Li6_7.c功能:以多文件的形式实现加、减、乘、除和求余数运算*/*将其它各源文件包含进来*/#include addition.c#include subtraction.c#include multiplication.c#include division.c#include remainder.c void main(),6.6.3 多个源文件的编译与连接,2023/5/11,6.7变量的作用域,局部变量也称为内部变量。局部变量是在函数内(包括函数说明和函数体)作定义说明的,其作用域仅限于函数内,离开该函数后再使用这种变量是非法的。【例6.8】局部变量的作用域 输入如下代码:/*源文件名:Li6_8.c功能:测试局部变量的作用域*/#include stdio.h void test(int a)int b=20;printf(%dn,a+b);,6.7.1 局部变量,2023/5/11,6.7变量的作用域,void main()int i=2,j=3,k;k=i+j;int k=8;printf(%dn,k);printf(%dn,k);test(k);,6.7.1 局部变量,2023/5/11,6.7变量的作用域,编译、连接、运行程序。程序运行后,屏幕显示:在函数test内定义了三个变量,a为形参,b为一般变量。在 test的范围内a、b有效,或者说a、b变量的作用域限于test内。同理,i,j,k的作用域限于main内。,6.7.1 局部变量,2023/5/11,6.7变量的作用域,关于局部变量的作用域还要说明以下几点:(1)主函数中定义的变量只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。(2)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。(3)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。(4)在复合语句中也可定义变量,其作用域只在复合语句范围内。,6.7.1 局部变量,2023/5/11,全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于整个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。,6.7.2 全局变量,2023/5/11,【例6.9】要求设计一个函数cuboid(double length,double width,double height)(3个参数依次为长方体的长、宽、高),用于求长方体的体积及正、侧、顶三个面的面积。算法设计要点:函数cuboid(int length,int width,int height)本身只能返回1个值(本案例选定体积),正、侧、顶三个面的面积就只能通过外部变量来进行数据共享。/*源文件名:Li6_9.c功能:求长方体的体积及正、侧、顶三个面的面积*/#include stdio.hdouble area1,area2,area3;/*定义3个外部变量,用于数据共享*/double cuboid(double length,double width,double height);/*函数说明*/,6.7.2 全局变量,2023/5/11,void main()double volume,length,width,height;printf(please input the cuboids length、width and height:);scanf(%lf%lf%lf,/*返回体积值*/,6.7.2 全局变量,2023/5/11,编译、连接、运行程序。程序运行后,屏幕显示:,6.7.2 全局变量,2023/5/11,注意:(1)外部变量的作用域:从定义点到本文件结束。为方便使用,建议将外部变量的定义放在文件开头,如例6.9所示。(2)在同一源文件中,允许外部变量和内部变量同名。在内部变量的作用域内,外部变量不起作用。(3)外部变量可实现函数之间的数据共享,但又使这些函数依赖这些外部变量,因而使得这些函数的独立性降低。从模块化程序设计观点来看,这是不利的。因此不是非用不可时,不要使用外部变量。,6.7.2 全局变量,2023/5/11,6.8 变量的存储类别,语言中的变量,不仅有类型特性,还有存储特性,从变量值存在的时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式。1动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。自动内部变量(auto)、寄存器变量(register)2静态存储方式:是指在程序运行期间分配固定的存储空间的方式。静态内部变量(static)、外部变量(extern)。用户存储空间可以分为三个部分:(1)程序区;(2)静态存储区;(3)动态存储区;,6.8.1 动态存储和静态存储,2023/5/11,6.8 变量的存储类别,全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序运行结束就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放;动态存储区存放以下数据:1)函数形式参数;2)自动变量(未加static声明的局部变量);3)函数调用时的现场保护和返回地址;对以上这些数据,在函数开始调用时分配动态存储空间,函数结束时释放这些空间。,6.8.1 动态存储和静态存储,2023/5/11,【例6.10】动态存储举例/*源文件名:Li6_10.c功能:测试动态存储变量的空间分配情况*/#include stdio.hvoid test()int m=10;m+;printf(m=%dn,m);,6.8.2 动态存储,2023/5/11,void main()printf(the first time:);test();printf(the second time:);test();printf(the third time:);test();,6.8.2 动态存储,2023/5/11,编译、连接、运行程序。程序运行后,屏幕显示:函数中的局部变量,如不特别声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto(auto可以省略)作存储类别的声明。如例6.10中,test函数中的m没有用static声明,说明它就是一个自动变量,所以,在主函数中调用了三次的test函数,每次得到的m的输出值都是一样的,这是因为自动变量只有在函数被调用的时候分配空间,当函数调用结束,空间也就自动释放了,所以每次调用m都是被重新赋初始值10。,6.8.2 动态存储,2023/5/11,【例6.11】静态局部变量例题/*源文件名:Li6_11.c功能:测试静态局部变量的空间分配情况*/#include stdio.hvoid test()static int m=10;/定义m为静态局部变量m+;printf(m=%dn,m);,6.8.3 用static声明的局部变量,2023/5/11,void main()printf(the first time:);test();printf(the second time:);test();printf(the third time:);test();,6.8.3 用static声明的局部变量,2023/5/11,编译、连接、运行程序。程序运行后,屏幕显示:有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为“静态局部变量”,用关键字static进行声明。如例6.11中test函数中的m变量被用static声明位静态局部变量。由于静态变量在程序运行期间的被分配的存储空间是固定的。所以第一次调用test函数的时候,m变量空间被分配,并且赋予初值10,然后执行m+操作,所以m的值就为11.但调用结束的时候m的空间仍然存在,没有因函数调用结束而被释放。所以在test函数第二次被调用的时候,m不再重新分配空间和初始化为10,而是使用原来的空间,沿用上一次的值11,故第二次调用的时候输出的m值为12,第三次为13。,6.8.3 用static声明的局部变量,2023/5/11,

    注意事项

    本文(第6章函数与预处理.ppt)为本站会员(sccc)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开