第四章模块化程序设计(函数)_第1页
第四章模块化程序设计(函数)_第2页
第四章模块化程序设计(函数)_第3页
第四章模块化程序设计(函数)_第4页
第四章模块化程序设计(函数)_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

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

文档简介

1、第四章模块化程序设计(函数)【计划课时】授课8课时+上机4课时(预习内容:教材第七章、第八章)一、概述回顾:·程序设计方法:自上而下,逐步细化·C语言:函数式语言在C程序设计中,通常: ·将一个大程序分成几个子程序模块(自定义函数)·将常用功能做成标准模块(标准函数)放在函数库中供其他程序调用如果把编程比做制造一台机器,函数就好比其零部件。·可将这些“零部件”单独设计、调试、测试好,用时拿出来装配,再总体调试。·这些“零部件”可以是自己设计制造/别人设计制造/现在的标准产品而且,许多“零部件”我们可以只知道需向它提供什么(如控制信号)

2、,它能产生什么(如速度/动力),并不需要了解它是如何工作、如何设计制造的所谓“黑盒子”。零部件(函数) 控制信号 速度/动力 (输入参数) (返回结果)【例】编写一个儿童算术能力测试软件显示软件封面检查密码产生题目接受回答评判计分显示结果如果要继续练习告别词main() char ans = y;clrscr();cover(); /*调用软件封面显示函数*/password(); /*调用密码检查函数*/while (ans =y| ans =Y) question(); /*调用产生题目函数*/ answers(); /*调用接受回答函数*/ marks(); /*调用评分函数*/ res

3、ults(); /*调用结果显示函数*/·这些函数现在不编程或还不会编程,可先放空。·可以多人合作,每人完成若干个函数(模块化)。·可在另一个源程序文件中定义。 printf(“是否继续练习?(Y/N)n”); ans=getch (); printf(“谢谢使用,再见!”);/*定义所用函数*/cover() /*软件封面显示函数*/password() /*密码检查函数*/question() /*产生题目函数*/answers() /*接受回答函数*/marks() /*评分函数*/results() /*结果显示函数*/函数使用常识: P1021、C程序执

4、行总是从main函数开始,调用其它函数后总是回到main函数,最后在 main函数中结束整个程序的运行。2、一个C程序由一个或多个源(程序)文件组成可分别编写、编译和调试。3、一个源文件由一个或多个函数组成,可为多个C程序公用。4、C语言是以源文件为单位而不以函数为单位进行编译的。5、所有函数都是平行的、互相独立的,即在一个函数内只能调用其他函数,不能再定义一个函数(嵌套定义)。6、一个函数可以调用其他函数或其本身,但任何函数均不可调用main函数。二、函数的定义函数定义“制造函数”1、无参函数 P102 定义格式:数据类型 函数名() /*现代风格是:函数名(void)*/ 函数体(说明部分

5、+ 语句)【注意】每个函数之前可以有自己的编译预处理命令组。数据类型为int时,可以省略。2、有参函数 P102定义格式:数据类型 函数名(形参表) /*现代风格是:函数名(带类型形参表)*/形参类型说明; 函数体(说明部分+ 语句)函数的返回值通过函数体中的return语句获得。形式: return (x); return (x+y); return (x>y?x:y); 语句中圆括号亦可省略。【注意】如果函数值类型与return语句表达式值的类型不一致,以函数类型为准(数值型会自动进行类型转换)。如果明确表示不需返回值,可用void作函数的数据类型。【例一】main() float

6、a=1.5,b=2.5; int c; c=max(a,b); printf(“Max is %dn”,c);max(x,y) /*用“值传递”分析法进行变量跟踪*/float x,y;float z; z=x>y?x:y; return z;结果:Max is 2 (编译通过,结果错误)【讨论】如何改错?·如果将输出语句中的%d改为%f,结果: Max is 0.125000 (亦错)·如果将max(x,y)改为float max(x,y),结果编译出错:Type mismatch in redeclaration of max 为什么?(原因见下)。三、函数的调用

7、函数和变量一样,在其主调函数中也必须“先说明,后使用”。注意关系: 函数定义制造函数函数使用说明(准备使用) 调用(使用函数)调用方式:·赋值 如:c=max(x,y); ·表达式中 c=1+max(x,y); printf(“Max=%dn”,max(x,y); ·执行函数 max(x,y);1、调用外部函数(其他源文件中定义的函数)时 函数说明语句 extern 函数名(); 【例】 文件file1.c中 main() int x=80,y=90,c; extern max(); /*函数说明*/ c=max(x,y)+20; /*调用max函数*/ prin

8、tf(“Max is %dn”,c);文件files2.c中(与file1.c同目录)extern max(int a,int b) /*extern可省*/float c; c=a>b?a:b; return c;2、调用同一源文件中的非标准函数时也必须在主调函数中对所调函数进行说明: 函数说明语句 数据类型 函数名(); (不说明会出现编译错误)但三种情况下可以省略说明: 函数值是整型(int)或字符型(char)时系统自动按整型说明; 所调函数的定义出现在主调函数之前时; 文件一开头,在所有函数之前,对所用函数作了说明3、函数的嵌套调用 P111四、函数的递归调用1、递归的概念 P

9、115直接递归调用 调用函数的过程中又调用该函数本身间接递归调用 调用f1函数的过程中调用f2函数,而f2中又需要调用f1。以上均为无终止递归调用。为此,一般要用if语句来控制使递归过程到某一条件满足时结束。2、递归法 类似于数学证明中的反推法,从后一结果与前一结果的关系中寻找其规律性。归纳法可以分为:·递推法 从初值出发,归纳出新值与旧值间直到最后值为止存在的关系 要求通过分析得到: 初值+递推公式 编程:通过循环控制结构实现(循环的终值是最后值)·递归法 从结果出发,归纳出后一结果与前一结果直到初值为止存在的关系 要求通过分析得到: 初值+递归函数编程:设计一个函数(递

10、归函数),这个函数不断使用下一级值调用自身,直到结果已知处选择控制结构其一般形式是:在主函数中用终值n调用递归函数,而在递归函数中:递归函数名f (参数x) if (n=初值) 结果=; else 结果=含f(x-1)的表达式; 返回结果(return);【例一】(P118例7.8)用递归法求n!分析比较:递推法递归法0!=11!=0!×12!=1!×23!=2!×3n!=(n1)!×n分析得Sn=n!的求解 1 (n=1,0)Sn=Sn-1×n (n>1)其中Sn-1先求出n!=(n1)! ×n(n1)!= (n2)! 

11、15;(n1)(n2)!= (n3)! ×(n2)(n3)!= (n4)! ×(n3)2!=1!×2分析得f(n)=n!的求解 1 (n=1,0)f(n)=f(n1)×n (n>1)其中f(n1)未求出实际上,递归程序分两个阶段执行回推(调用):欲求n! 先求 (n-1)! (n-2)! 1! 若1!已知,回推结束。递推(回代):知道1!2!可求出3! n!程序如下:main() int n; float s; float fac(); clrscr(); printf("Input n="); scanf("%d&q

12、uot;,&n); s=fac(n); printf("%d!=%.0f",n,s);float fac(int x)int f; if (x=0|x=1) f=1; else f=fac(x-1)*x; return f; 执行:Input n=5结果:5!=120【例二】(P116例7.7)有5个人,第5个人说他比第4个人大2岁,第4个人说他对第3个人大2岁,第3个人说他对第2个人大2岁,第2个人说他比第1个人大2岁,第1个人说他10岁。求第5个人多少岁。分析: 10 (n=1) age(n)= age(n-1)+2 (n>1)程序如下:main()clr

13、scr(); printf("%d",age(5);age(int n) int c; if (n=1) c=10; else c=age(n-1)+2; return c;结果:18【例三】在屏幕上显示杨辉三角形 1 分析:若起始行为第1行1 1 则: 第x行有x个值1 2 1 对第x行第y列,其值(不计左侧空格时)1 3 3 1 1 (y=1 或 y=x) 1 4 6 4 1 c(x,y)=1 5 10 10 5 1 c(x-1,y-1)+c(x-1,y) 程序如下:main()int i,j,n;clrscr();printf("Input n="

14、);scanf("%d",&n);for (i=1;i<=n;i+) for (j=0;j<=n-i;j+) printf(" "); /*为了保持三角形态,此处输出两个空格*/ for (j=1;j<=i;j+) printf("%3d ",c(i,j); printf("n"); int c(int x,int y)int z;if (y=1|y=x) return 1;else z=c(x-1,y-1)+c(x-1,y); return z; 【例四】Fibonacci数列问题。 1

15、 (n=1)分析:fib(n)= 1 (n=2) fib(n-1)+fib(n-2) (n>1)程序如下:fib (int n) int f; if (n=1|n=2) f=1; else f=fib(n-1)+fib(n-2); return (f);main() int i,s=0; clrscr(); for (i=1;i<=12;i+) s=s+fib(i); printf("n=12,s=%d",s);结果:376【例五】运行下列程序,当输入字符序列AB$CDE并回车时,程序的输出结果是什么?#include <stdio.h>rev()

16、char c; c=getchar(); if (c='$') printf("%c",c); else rev(); printf("%c",c); main()rev();结果:$BA【例六】反向输出一个整数(非数值问题)非数值问题的分析无法象数值问题那样能得出一个初值和递归函数式,但思路是相同的。分析方法:简化问题:设要输出的正整数只有一位,则“反向输出”问题可简化为输出一位整数。对大于10的正整数,逻辑上可分为两部分:个位上的数字和个位以前的全部数字。将个位以前的全部数字看成一个整体,则为了反向输出这个大于10的正整数,可按以下步

17、骤: a、输出个位上的数字; b、将个位除外的其他数字作为一个新的整数,重复a步骤的操作。其中b问题只是对原问题在规模上进行了缩小递归。所以,可将反向输出一个正整数的算法归纳为: if (n为一位整数) 输出n; else 输出n的个位数字; 对剩余数字组成的新整数重复“反向输出”操作; 程序如下:#include <stdio.h>void main() void printn(int x); int n; printf("Input n="); scanf("%d",&n); if (n<0) n=-n;putchar(&#

18、39;-'); printn(n);void printn(int x) /*反向输出整数x*/if (x>=0&&x<=9) /*若x为一位整数*/ printf("%d",x); /*则输出整数x*/else /*否则*/printf("%d",x%10); /*输出x的个位数字*/ printn(x/10); /*将x中除个位数字外的全部数字形成新的x后,继续递归操作*/ 执行:Input n=12345结果:54321执行:Input n=12479结果:97421【讨论】Input n=123456 6167

19、为什么:123456710=1 1110 0010 0100 00002 int类型的数实际只能存入16位,即1110 0010 0100 00002 第1位为符号位,其余各位取反后再加1,得原码1001 1101 1100 0000 (真值-761610)【例七】汉诺塔(Tower of Hanoi)问题。 P118例7.9 试题汇编【7.18】也是一个非数值问题。分析方法:简化问题:设盘子只有一个,则本问题可简化为ab。对于大于一个盘子的情况,逻辑上可分为两部分:第n个盘子和除n以外的n-1个盘子。如果将除n以外的n-1个盘子看成一个整体,则要解决本问题,可按以下步骤: a、将a杆上n-1

20、个盘子借助于b先移到c杆; ac (n-1,a,c,b) b、将a杆上第n个盘子从a移到b杆; ab c、将c杆上n-1个盘子借助a移到b杆。 cb (n-1,c,b,a)五、变量的存储类型 模块化设计要求研究不同模块(函数、源文件)间变量的关系。变量两大属性: ·数据类型 ·存储类别1、数据类型(复习)通过变量说明(定义)来规定其数据类型: 格式 数据类型关键字 变量名 如 char a; int b,c ; float x,y;为什么使用变量前要先对其数据类型进行“说明”好比看电影前先买票订座 ·预留存储空间(如char型为1个字节,int型为2个字节)

21、83;确定存储方式(如char型存放ASCII值,int型存放补码值)011000011 char型 (用一个字节存放该字符的ASCII值)0110000111100110 int 型 (用两个字节存放该数值的补码) 011010101010001010101010 011000011 float型 (四个字节) 尾数(补码) 阶码一个变量的数据类型定义后,就规定了该变量只能存储相应类型的数据。如定义int x,则x只能存放整型数,如果x=3.14159,会被自动转为整型存放。2、存储类别 P130 规定了变量在计算机内部的存放位置决定变量的“寿命”(何时“生”,何时“灭”) 一个完整的变量说

22、明格式如下: 存储类别 数据类型 变量名 如 static int x , y ; CPU RAM运算器+控制器寄存器代码区常量区静态存储区动态存储区 用户区数据区存储类别:·register型(寄存器型) 变量值存放在运算器的寄存器中存取速度快,一般只允许23个,且限于char型和int型,通常用于循环变量(在微机的Turbo C中实际上自动转为auto型)·auto型(自动变量型)变量值存放在主存储器的动态存储区(堆栈方式)优点同一内存区可被不同变量反复使用以上两种均属于“动态存储”性质,即调用函数时才为这些变量分配单元,函数调用结束其值自动消失。·stati

23、c型(静态变量型)变量值存放在主存储器的静态存储区程序执行开始至结束,始终占用该存储空间·extern型(外部变量型)同上,其值可供其他源文件使用以上两种均属于“静态存储”性质,即从变量定义处开始,在整个程序执行期间其值都存在。未说明存储类别时,函数内定义的变量为auto型,函数外定义的变量为extern型。六、局部变量与全局变量1、局部变量函数内部或复合语句内定义的变量 auto(默认) 所在函数调用结束时,其值自动消失 局部变量 register 如不赋初值,取不确定值为初值 static 所有函数调用结束,其值仍保留如不赋初值,取初值为0(数值型)或空格(字符型) 所有形参都是

24、局部变量 局部变量只在本函数或本复合语句内才能使用,在此之外不能使用(视为不存在)main函数也不例外。【例一】求程序运行结果main() int a=2,i; clrscr(); for (i=0;i<3;i+) printf("%4d",f(a);f(int a) int b=0;static int c=3; b+;c+; return a+b+c; a i b c f(a) 2 0 01 4 71 01 5 82 01 6 9【结果】 7 8 9【例二】求程序运行结果main() int k=4,m=1,p; clrscr(); p=func(k,m); pr

25、intf("%d,",p); p=func(k,m); printf("%d",p);func(int a,int b) static int m=0,i=2; i+=m+1; m=i+a+b; return m; k m a b m i 4 1 4 1 08 23 4 1 4 1 817 312【结果】 8,172、全局变量在函数之外定义的变量 extern(默认) 允许本源文件中其他函数及其他源文件使用 全局变量 static 只限本源文件中使用有效作用范围:从定义变量位置开始直到本源文件结束如果需要将全局变量的作用范围扩展至整个源文件法1:全部在源

26、文件开头处定义 法2:在引用函数内,用extern说明 P129例7.16 法3:在源文件开头处,用extern说明【例三】求程序运行结果extern int x,y; /*可以省略int。如果没有此句,编译就会出错*/ clrscr();printf("x=%d,y=%dn",x,y);int x=100,y=200;如果要将全局变量作用范围扩展到其他源文件,只需在使用这些变量的文件中对变量用extern加以说明。【注意】1、 所有全局变量加不加static,都属于静态存储,如不赋初值,取初值为0(数值型)或空格(字符型)(注意与函数内部定义的static型局部变量的区别

27、)2、 如果在同一个源文件中,全局变量与局部变量同名,则在局部变量作用范围内,全局变量不起作用。 P129【例四】求程序运行结果int a=3,b=5;max(int a,int b) int c; c=a>b?a:b; return c; main() int a=8; printf("%dn",max(a,b); 结果:8 【讨论】如果主函数中没有int a=8,结果?(5)如果让主函数中int a=4或a=-1,结果?(均为5)【例五】求程序运行结果void num() extern int x,y; int a=15,b=10; x=a-b; y=a+b;in

28、t x,y;main() int a=7,b=5; x=a+b; y=a-b; num(); printf("%d,%dn",x,y); 结果:5,25【讨论】如果第二行不加上extern呢?(12,2)【例六】求程序运行结果int a;fun(int i) a+=2*i;return a; main() int a=10; printf("%d,%dn",fun(a),a);结果:20,10【例七】求程序运行结果int i=0;main() int i=5; clrscr(); reset(i/2); printf("i=%dn",

29、i); /*此处i为局部变量值,即i=5*/ reset(i=i/2); printf("i=%dn",i); /*局部变量i=i/2,即i=2*/ reset(i/2); printf("i=%dn",i); /*局部变量i 的值未变*/ workover(i); printf("i=%dn",i); /*局部变量i 的值未变*/workover(int i) i=(i%i)*(i*i)/(2*i)+4); printf("i=%dn",i); return i;reset(int i) /*此处形参i使用主函数

30、中局部变量i的值*/ i=i<=2?5:0; /*此处i为全部变量*/ printf("i1=%d ",i); return i;结果:i1=5 i=5 i1=5 i=2 i1=5 i=2 i=0 i=2 【讨论】如果将主函数中第二个reset(i/2)改为reset(i=3)结果:1=5 i=5i1=5 i=2i1=0 i=3i=0i=3七、编译预处理编译预处理:宏定义/文件包含/条件编译【编译】C编译系统对源程序进行:词法和语法分析,代码生成,优化 .OBJ文件【编译预处理】编译前对源程序进行一些预加工(改善程序设计环境/模块化设计)编译预处理命令均以#开头,未尾

31、不加分号可出现在程序的任何位置,其作用范围:出现点至所在源程序未尾。1、宏定义格式 #define 宏名 宏体宏名和宏体均为字符串,前者必须符合标识符命名规则。预处理时在程序中用宏体替换宏名。注意:可以用 #undef 宏名 终止该宏名的作用范围 定义符号常量【例一】#define M 3#define N (M+1)#define NN N*N/2main()clrscr();printf("NN=%d,",NN);printf("5*NN=%dn",5*NN);结果:NN=8,5*NN=40【讨论】如果第二行改为 #define N M+1 结果?(NN=6,5*NN=18)带参数的宏定义格式 #define 宏名(参数表) 宏体【例二】#defi

温馨提示

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

评论

0/150

提交评论