C语言程序设计课件:5 多函数程序设计_第1页
C语言程序设计课件:5 多函数程序设计_第2页
C语言程序设计课件:5 多函数程序设计_第3页
C语言程序设计课件:5 多函数程序设计_第4页
C语言程序设计课件:5 多函数程序设计_第5页
已阅读5页,还剩65页未读 继续免费阅读

下载本文档

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

文档简介

1、1,上次作业中的问题,思路不清,尤其是迭代法 两种理解: 计算完这一次,还要为下一次准备数据。 每次都要完成的工作,除了按照迭代公式(方法)处理数据以外,还有其它的步骤。 对于多重穷举,要一层一层地来,别忘了在循环体中加上必要的,第5部分,多函数程序设计,3,先看一个大型实例,程序的结构: 编译预处理命令 其它必要的定义 其它函数声明 主函数 其它函数定义 结论: C程序是由很多个函数组成的。 C语言中关于函数有三个主要内容: 函数定义 函数调用 函数声明,4,为什么定义函数?,大型任务总要由多人完成,因此,在编程之前,一定要将任务划分成多个功能独立的模块,再分别分配给多个程序分别编程实现。

2、函数可以复用,以节省开发时间。每个函数,就象一块雕刻好的积木,可以直接用来构建新的程序。,5,模块化的几个原则,模块分解的原则 保证模块的相对独立性 高聚合:一个模块只能完成单一的功能,代码一般几十行。 低耦合:指模块之间参数传递尽量少,尽量不通过全局变量来实现数据传递 信息隐藏 把所有用户不需要关心的细节隐藏至模块内部。,6,我们怎么做?,关键是如何分段。 比较独立的、完整的功能分为一个函数,一般函数十几行。 函数定义时注意与被调函数之间的沟通与联系,即参数传递与返回两个方向的数据流动。 在讲例题的时候请注意这两点,7,例1:定义一个函数,求梯形面积,先完成一个数学函数的定义: s(a,b,

3、h)=(a+b)*h/2 自变量 函数名 函数公式 编写函数必须考虑的三个内容: 先来考虑这个任务需要什么必要的数据,都是什么类型?(形式参数) 有没有结果,结果又是什么类型?(返回值) 应该完成什么功能?如何实现?(函数功能),8,分析结果,/*函数功能:求梯形面积 函数形式参数: float a表示上底 float b表示下底 float h表示高 函数返回值:梯形面积(float类型) */,9,涉及的语法函数定义格式,/*函数功能:实现功能 函数形式参数:参数1,表示 参数2,表示 . 函数返回值: */ 返回值类型 函数名(形式参数列表) 函数体 ,养成注释的 好习惯:,10,函数定

4、义,/*函数功能:求梯形面积 函数参数: float a表示上底,float b表示下底, float h表示高 函数返回值:梯形面积*/ float Area(float a,float b, float h) int s = (a+b)*h/2 ; return s; ,三个形式参数,返回值类型,函数名称,返回语句,函数体,函数定义相当于建立一个s(a,b,h)=(a+b)*h/2这样一个函数的形式化表示,此时没有具体的数值,只有代入数值才能计算出数值。,11,在main函数中怎样调用呢?,#include float s(float,float,float);/*函数声明*/ float

5、 s(float a,float b, float h) return (a+b)*h/2; void main() float a,b,h,area; printf( please input a,b,h: ); scanf(%f%f%f, ,12,运行过程,#include float s(float,float,float);/*函数声明*/ float s(float a,float b, float h) return (a+b)*h/2; void main() float a,b,h,area; printf( please input a,b,h: ); scanf(%f%f%

6、f, ,printf( area=%fn, s(a,b,h) );,13,涉及的语法函数调用(call),调用即使用已经定义好的函数。 调用函数时,必须提供所有的参数 printf和scanf是采用变长变量表定义的函数,所以变量的个数不固定。 提供的参数个数、类型、顺序应与定义时相同 单向值传递,14,涉及的语法函数调用格式,函数调用的一般形式: 函数名(实参列表) 具体调用格式: 有返回值时 放到一个数值表达式中,如area = s(a,b,h); 作为另一个函数调用的参数,如 printf(area=%fn, s(a,b,h) ); 或者直接参与运算:如: F= pow(a,b) *c 无

7、返回值时 函数调用表达式,如printf(hello!); 注意:main()函数可以调用其它函数,但它不可以被其它函数调用。即:它只能作为主调函数,不能作为被调函数,15,涉及的语法函数原型或函数声明,调用一个函数之前,先要对其返回值类型、函数名和参数进行声明(declare) 不对函数进行声明是非常危险的 声明时不要省略参数以及返回值的类型 一般都写在编译预处理命令之后。,16,函数的分类,从使用角度: 库函数 :直接使用(调用) 自定义函数:用户需要自己先定义,然后使用。 根据返回值: 有返回值:需要返回计算结果 无返回值:不需要返回计算(处理)结果 根据参数: 无参函数:clrscr(

8、) 有参函数:pow(x,y),17,函数声明,函数声明也称为函数原型 在调用之前一定要对函数进行声明,否则在编译时会出现function not define!的提示 如果函数定义在函数调用之前,函数声明可以省略。,18,也可以写成,#include /*函数功能:计算梯形面积并输出 函数形式参数: float a表示上底,float b表示下底, float h表示高 函数返回值:无*/ void Area(float a,float b, float h) printf(area=%f, (a+b)*h/2); void main() float a,b,h; printf( pleas

9、e input a,b,h: ); scanf(%d%d%d, /*调用函数计算面积并输出*/ ,19,多函数程序的调试方法,要想检验一个函数是否正确,要在调用函数处设置断点。当调试执行时,在该行停止,此时,点击build-step into进入函数内部执行,此时可点击build-step over单步跟踪执行,或在调试前在函数内部重点检查语句行设置断点。若想结束函数内部的执行,点击build-step out,回到主调函数。,20,练习1:编写函数,将小写字母转换成大写字母,#include char atoA(char);/*函数声明*/ /* 函数功能:将小写字母转换成大写字母 函数形式

10、参数:小写字母char lower 函数返回值:转换成的大写字母,char类型*/ char atoA(char lower) return (lower-32); void main() char lower,upper; printf( please input an lowercase: ); scanf(%c, ,21,#include #include int Prime(int m)/*判断m是否为素数,是返回1,否返回0*/ int i,find = 0, k = sqrt(m); for (i=2; i=k ,例2:判断2-100之间哪些是素数,算法?,22,练习2:求两个数最

11、大公约数,#include int Gcd(int,int); /*函数声明*/ int Gcd(int a,int b)/*函数功能:求两数的最大公约数*/ int c; do c=a%b; a=b; b=c; while(c!=0); return a; void main() int a, b, c; printf(please enter two integers:); scanf(%d%d, ,23,小结1,要求 能够编写多函数程序,编写函数之前要写清楚注释(包括函数功能,函数形参,函数返回值) 会调用函数,理解函数的调用过程和整个程序的执行顺序 理解数据在函数调用过程中和返回时的流

12、动。 应掌握的语法内容 函数的定义、调用、声明格式(重点中的重点),24,作业1,定义一个求n!的函数,并在主函数中调用它,计算1!+m!. 定义两个函数,分别计算圆的周长和面积,并在主函数中调用这两个函数。,25,#include double Fac(int n); void main() int i,n; double sum; printf(please input n:); scanf(%d, ,作业答案,26,#include #define PI 3.14 float l(float); /*函数声明*/ float s(float); /*函数声明*/ float l(floa

13、t r) /*函数定义:求周长*/ return PI*r*2; float s(float r) /*函数定义:求面积*/ return PI*r*r; void main() float r, l, s; printf(please input r:); scanf(%f, ,问题,若这样起名会怎样?,会有错误提示,因为相同的名字造成了冲突。,27,#include #define PI 3.14 float l(float); /*函数声明*/ float s(float); /*函数声明*/ float l(float r) /*函数定义:求周长*/ return PI*r*2; fl

14、oat s(float r) /*函数定义:求面积*/ return PI*r*r; void main() float r, cir, area; printf(please input r:); scanf(%f, ,问题,若这样起名又会怎样?会冲突吗?,不会,它们虽然是不同的r,但因为它们的作用域不同,所以不会产生冲突。,28,涉及的语法-作用域,作用域:即作用范围 可分为: 局部变量 全局变量,29,局部变量,局部变量 在语句块内(即 内)定义的变量 形式参数也是局部变量 特点 定义时不会自动初始化,除非程序员指定初值 进入语句块时获得内存,仅能由语句块内语句访问,退出语句块时释放内存

15、,不再有效 并列语句块各自定义的同名变量互不干扰,30,main() int i=1,j=2; printf(i=%d,j=%dn,i,j); int i=3,a=4; printf(i=%d,a=%dn, i,a); j+; int i=5; printf(i=%dn,i); j+; printf(i=%d,j=%dn,i,j); ,运行结果为: i=1,j=2 i=3,a=4 i=5 i=1,j=4,例,运行结果?,+是一个运算符,j+表示将j变量的值加1,31,例3,运行结果?,#include void swap(int x,int y); /* 函数声明 */ void main(

16、) int a=3,b=5; printf(11a=%d, b=%dn,a,b);/* 调用交换函数之前 */ swap(a,b); /* 调用交换函数swap */ printf(12a=%d, b=%dn,a,b);/* 调用交换函数之后 */ /* 函数定义:交换两个变量的值的函数 */ void swap(int a,int b) int temp; printf(21 a=%d, b=%dn,a,b); /* 交换变量值之前 */ temp=a; a=b; b=temp; /* 交换器:交换变量x,y的值 */ printf(22 a=%d, b=%dn,a,b); /* 交换变量值

17、之后 */ ,3,5,3,实参a,b的值没发生变化! 单向值传递! 即由实参向形参的方向传递数值!而不会朝相反的方向!,32,全局变量,全局变量 在所有函数之外定义的变量 特点 默认作用范围:在源程序.c中,从定义它的位置以后都有效 在定义点之前或在其他.c文件中引用,应该进行如下声明: extern 类型名 变量名; 从程序运行起即占据内存,程序运行过程中可随时访问,程序退出时释放内存 使函数之间的数据交换更容易,也更高效 但是尽量少用,因为谁都可以改写全局变量,所以很难确定是谁改写了它 破坏了函数的独立性(封装性),33,#include int global;/*定义全局变量*/ voi

18、d GlobalPlusPlus(void); main() global = 1; printf(Before GlobalPlusPlus(), it is %dn, global); GlobalPlusPlus(); printf(After GlobalPlusPlus(), it is %dn, global); /* 函数功能: 对全局变量global加1,并打印加1之前与之后的值 函数入口参数: 无 函数返回值: 无 */ void GlobalPlusPlus(void) printf(Before +, it is %dn, global); global+; printf

19、(After +, it is %dn, global); ,例4-1,Before GlobalPlusPlus(), it is 1 Before +, it is 1 After +, it is 2 After GlobalPlusPlus(), it is 2,注意:全局变量具有记忆性 。,34,#include void GlobalPlusPlus(void); main() int global = 1; printf(Before GlobalPlusPlus(), it is %dn, global); GlobalPlusPlus(); printf(After Glob

20、alPlusPlus(), it is %dn, global); /* 函数功能: 对局部变量global加1,并打印加1之前与之后的值 函数入口参数: 无 函数返回值: 无 */ void GlobalPlusPlus(void) int global = 1; printf(Before +, it is %dn, global); global+; printf(After +, it is %dn, global); ,例4-2,Before GlobalPlusPlus(), it is 1 Before +, it is 1 After +, it is 2 After Glob

21、alPlusPlus(), it is 1,35,如何用全局变量解决例3,#include int a=3,b=5; void swap(void); /* 函数声明 */ void main( ) printf(11 a=%d, b=%dn,a,b);/* 调用交换函数之前 */ swap(); /* 调用交换函数swap */ printf(12 a=%d, b=%dn,a,b);/* 调用交换函数之后 */ void swap(void ) /* 函数定义,没有参数 */ int temp; printf(21 a=%d, b=%dn,a,b); /* 交换变量值之前 */ temp=a

22、; a=b; b=temp; /* 交换变量a,b的值 */ printf(22 a=%d, b=%dn,a,b); /* 交换变量值之后 */ ,虽然得以解决,但并不是一个好办法! 为什么? 因为它破坏了函数的封闭性! 学指针的时候我们会学另外一种方法! 即便要用全局变量,一般也是在只读不写的时候才用!,36,例:使用全局变量解决,#include #define PI 3.14 float r; float l( void ); /*函数声明*/ float s(void); /*函数声明*/ float l(void) /*函数定义:求周长*/ return PI*r*2; float

23、s(void) /*函数定义:求面积*/ return PI*r*r; void main() float cir, area; printf(please input r:); scanf(%f, ,r一旦被赋值,其值不再发生变化,只是拿来用。,37,另一个具有记忆性的变量类型:静态变量(static),一般的内部变量 在函数退出后失效,再次进入函数,变量值重新初始化 静态变量 在变量类型前面用static修饰 static int i; 变量存在静态存储区,当函数结束时,内存空间不被释放,因此,变量的值可以保存到下次进入函数,即变量具有记忆功能,38,涉及的语法-变量的存储类型,编译器为变

24、量分配内存的方式 它决定变量的生存期,动态存储 根据需要临时分配存储空间,离开即释放 静态存储 在程序运行期间分配固定的存储空间不释放,内存分配,39,根据存储类型可分为,自动变量(auto) 静态变量(static) 寄存器变量(register),40,例-静态变量,#include void Func(void); void main() int i; for (i=0; i10; i+) Func(); /* 函数功能: 打印被调用的次数 函数入口参数: 无 函数返回值: 无 */ void Func(void) static int times = 1;/*静态局部变量 函数结束时t

25、imes变量仍然占据静态存储区的存储空间,不释放*/ printf(Func() was called %d time(s).n, times+); ,Func() was called 1 time(s). Func() was called 2 time(s). Func() was called 3 time(s). Func() was called 4 time(s). Func() was called 5 time(s). Func() was called 6 time(s). Func() was called 7 time(s). Func() was called 8 t

26、ime(s). Func() was called 9 time(s). Func() was called 10 time(s).,2,3,10,41,例-非静态变量,#include void Func(void); void main() int i; for (i=0; i10; i+) Func(); /* 函数功能: 打印被调用的次数 函数入口参数: 无 函数返回值: 无 */ void Func(void) int times = 1; printf(Func() was called %d time(s).n, times+); ,Func() was called 1 tim

27、e(s). Func() was called 1 time(s). Func() was called 1 time(s). Func() was called 1 time(s). Func() was called 1 time(s). Func() was called 1 time(s). Func() was called 1 time(s). Func() was called 1 time(s). Func() was called 1 time(s). Func() was called 1 time(s).,2,2,42,自动变量 (auto ),我们以前定义的那些变量,都

28、默认是这种类型 自动体现在 进入语句块时自动申请内存,退出时自动释放内存 标准定义格式 auto 类型名 变量名; 特点: 动态局部变量 缺省的存储类型 不初始化时,值是不确定的,43,寄存器变量(register),寄存器 CPU的内部容量很有限、但速度极快的存储器 使用频率比较高的变量声明为register ,可以使程序更小、执行速度更快 register 类型名 变量名; register int i; 现代编译器有能力自动把普通变量优化为寄存器变量,并且可以忽略用户的指定,所以一般无需特别声明变量为register,44,静态变量和全局变量,相同点:都是静态存储类型 自动初始化为0 都

29、存储在静态存储区,整个程序运行期间一直占据内存 不同点:作用域不同 全局变量在所有的源程序文件中都可用 静态变量又分为静态局部变量和静态全局变量,作用域分别是所在函数和所在源文件,45,读程序功能,#include double Fac(int n); void main() int i,n; double result; printf(please input n:); scanf(%d, ,程序功能: 求1-n的阶乘 分析: 虽然函数中只乘了一个数,但由于是static变量,所以“记住”了以前乘的结果,46,#include int square(int i); void main() i

30、nt i=0; i=square(i); for(;i3;i+) static int i=1; i+=square(i); printf(%d,i); printf(%dn,i); int square(int i) return i*i;,读程序结果,2,1,6,2,42,3,输出结果:,2,6,3,42,注意:搞清楚三个I的作用域和存储类型。,47,小结2,按变量作用域,可分为局部变量和全局变量。 一般情况下用局部变量,局部变量可以重名,不冲突,但各自为政。 极少情况下,当需要考虑到数据的一致性时可以用全局变量。全局变量具有记忆性,所以,一般不随意修改它的值。 按存储类型分:可分为aut

31、o、static、register变量。 static 具有记忆性,一般用做局部变量。 auto、register一般不用 。,48,例5,例:求n! 1 n=1 n*(n-1)! n1,long fac(int n) if(n=1) return 1; else return n*fac(n-1); ,写成函数?,49,例5程序,#include long fac(int n) if(n=1) return 1; else return n*fac(n-1); main() int n; Printf(please input a number:n); scanf(%d, ,f(10) re

32、trun 10*f(9 ); ,f(9) retrun 9*f(8 ); ,f(1) return 1; ,f(2) return 2*f(1); ,执行过程?,递归:函数嵌套调用自身,50,递归 迭代,long int fac(int n) if(n=1) return 1; else return n*fac(n-1); ,long int fac(int n) Long fc=1; int i; for(i=1;i=n;i+); fc=fc*i; return fc; ,51,递归与迭代的比较,n! 1 n=1 n*(n-1)! n1,n!=(1*2)*3)*n),52,什么问题可以用递

33、归来解决?,问题具有如下特点: 问题较复杂,不易用迭代法直接求解 该问题可以分解成若干子问题,子问题除了规模较原问题小以外,其它均相同。 最终,总有一个问题不能再分解。,53,例5-4 求解Hanoi(汉诺)塔问题,古代有一个梵塔,塔内有三个柱子A、B、C,僧侣们想把A拄子上的一摞盘子移动到C柱子上。最初A拄子上有大小不等的64个盘子,且小的在上,大的在下。在移动过程中,大盘子只能在下,小盘子只能在上,并且每次只能移动一个盘子,可以借助于B柱子。,54,问题分析,解决Hanoi(汉诺)塔问题的方法可以表述如下: 老和尚移动64个盘子的步骤 第1步,请第2个和尚将前63个盘子从A柱子移到B柱子;

34、 第2步,自己将最下面的第64个盘子从A柱子移到C柱子; 第3步,再请第2个和尚将63个盘子从B柱子移到C柱子。 第2个和尚移动63个盘子的步骤 第1步,请第3个和尚将前62个盘子从A柱子移到C柱子; 第2步,自己将最下面的第63个盘子从A柱子移到B柱子; 第3步,再请第3个和尚将62个盘子从C柱子移到B柱子。 依此类推,直到第63个和尚完成了2个盘子的移动,最后由第64个和尚完成1个盘子的移动。这个过程称之为回推过程。,每个人的工作是:移动n个盘子从A到B(借助C)的步骤 第1步,请别人将n-1个盘子从A柱子移到C柱子; 第2步,自己将最下面的第n个盘子从A柱子移到B柱子; 第3步,再请别人

35、将n-1个盘子从C柱子移到B柱子。 一个特例:当n=1的时候,55,move函数,/* 定义函数:显示移动过程 int no:表示第no个盘子 char from:表示源柱子 char to:表示目的柱子 */ void move(int no,char from,char to) printf(Move %3dth disk:%c -%cn,no,from,to); ,56,hanoi函数,/* 定义函数: 借助by柱子将n个盘子从from柱子移动到to柱子 int n:表示n个盘子 char from:表示源柱子 char to:表示目的柱子,char by:表示要借助的柱子 */ voi

36、d hanoi(int n,char from,char by,char to) if (n=1) move(n,from,to); else hanoi(n-1,from,to,by); move(n,from,to); hanoi(n-1,by,from,to); ,57,#include /* 包含头文件 */ void move(int,char,char); /* 自定义函数的声明 */ void hanoi(int n,char,char,char); /* 自定义函数的声明 */ void main() /* 主函数,无参数,无返回值 */ int n; /* 定义整型变量n,存

37、放盘子总数 */ printf(Input the number of diskes: ); /* 提示输入n的值 */ scanf(%d, ,完整程序,执行过程? 作业:请大家用讲过的单步执行方法观察一下n为3的执行过程,58,运行输出:,Input the number of disks: 3 The step to moving 3 disks: Move 1: A - B Move 2: A - C Move 3: B - C Move 4: A - B Move 5: C - A Move 6: C - B Move 7: A - B,59,语法:函数的递归调用,在调用一个函数的过程

38、中又直接或间接地调用函数本身,f( ) f( ); ,f1( ) f2( ); ,f2( ) f1( ); ,特殊形式的函数嵌套,重点,直接递归,间接递归,60,练习3:写递归函数计算fabonacci数列的第n项,61,练习3程序,#include long fib(int n); /* 自定义函数声明 */ void main() long s; /* 第 i 项斐波那契数列的值 */ int i=0; /* 斐波那契数列某项的序号 */ do s=fib(i); /* 求斐波那契数列第 i 项 */ printf(Fib(%d)=%ldn,i,s); /* 显示斐波那契数列第 i 项的值

39、 */ printf(Input Fibonacci Number,0 means exiting:); scanf(%d, /* 求斐波那契数列的递归方式 */ ,62,带参数的宏定义,#define SQUARE(n) (n)*(n) void main() int i=1; for( ;i=10;i+) printf(%4d, SQUARE(i) ); ,void main() int i=1; for( ;i=10;i+) printf(%4d, (i)*(i) ); ,编译前 替换为:,63,区别,(i)*(i),当函数功能非常简单时,可以用带参数的宏定义来实现。,64,涉及语法-带参数的宏定义,一般格式: #define 宏名(参数表) 字符序列 功能:将程序中出现的前者置换为后者。 其中宏名后面的括号里是参数,类似函数中形参表,只是此处的形参无类型说明。字符序列中应包含括号中所指定的参数,否则参数设置无意义。,65,应注意的问题,使用带参数的宏定义可以实现某些简单函数的功能(注意是某些,而不是全部)。 定义时,宏名和参数表之间不能有空格。 对带参数的宏定义,字符序列及其字符序列中各个形参都应该用圆括号括起来。 例:#define SQ

温馨提示

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

评论

0/150

提交评论