计算机精品课件:第6章_函数_第1页
计算机精品课件:第6章_函数_第2页
计算机精品课件:第6章_函数_第3页
计算机精品课件:第6章_函数_第4页
计算机精品课件:第6章_函数_第5页
已阅读5页,还剩224页未读 继续免费阅读

下载本文档

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

文档简介

1、1,第6章 函数,6.1 C语言函数概述 6.2 函数的定义 6.3 函数的调用 6.4 函数的嵌套调用与递归调用 6.5 用数组做函数参数 6.6 局部变量和全局变量 6.7 变量的存储类别 6.8 C语言预处理,2,程序一般应分为若干个程序模块; 每个模块用来实现一个特定的功能; 用子程序实现模块的功能; 在C语言中子程序的作用是由函数完成的,6.1 C语言函数概述,3,一个C源程序可由一个主函数和若干个函数组成; 主函数可以调用其他函数; 其他函数不能调用主函数; 其他函数也可互相调用; 同一个函数可以被一个或多个函数调用任意多次,4,例:要求输出以下的结果。 * How do you

2、do!,include void main() printstar(); print_message(); printstar();,在输出中分别有2行“*”号,显然不必重复写这段代码,5,printstar() printf(“* * * * * * * * * *n”);,print_message() printf(“How do you do !n”);,6,说明: 1)一个C程序由多个源程序文件组成; 2)一个源程序文件是一个编译单位; 可分别编写,分别编译; 一个源文件由一个或多个函数组成; 3)C程序的执行从main函数开始; 调用其他函数后,流程回到main函数; 并在main

3、函数中结束整个程序的运行,7,4)所有函数是平行的; (即在定义函数时是相互独立的) 函数不能嵌套定义; (即一个函数并不从属于另一个函数) 函数间可以互相调用; (但不能调用main函数,8,5)从用户的角度看,函数有两种: 标准函数(库函数) 由系统提供。 用户自定义的函数 用以解决用户的专门需求,9,6)从函数的形式看,函数有两类: 无参函数 在调用无参函数时,主调函数不把数据传送给被调函数; 无参函数可以带回或不带回函数值。 有参函数 在调用有参函数时,在主调和被调函数间有数据传递,10,C语言要求,在程序中用到的所有函数,必须“先定义,后使用” 指定函数名字、函数返回值类型、(函数实

4、现的功能)以及参数的个数与类型,将这些信息通知编译系统,6.2 函数的定义,11,指定函数的名字,以便以后按名调用 指定函数类型,即函数返回值的类型 指定函数参数的名字和类型,以便在调用函数时向它们传递数据 指定函数的功能。这是最重要的,这是在函数体中解决的,12,对于库函数,只需用#include指令把有关的头文件包含到本文件模块中即可; 需要在程序中自己定义库函数中没有提供的函数自定义函数,13,无参函数的定义形式 类型标识符 函数名() 声明部分 语句,类型标识符: 规定函数值的类型。Void不需返回值 声明部分: 定义函数体内各变量及其类型。 语句: 完成函数要处理的内容,6.2.1

5、定义函数的一般形式,14,2. 有参函数定义的一般形式 类型标识符 函数名(形式参数列表) 声明部分 语句,15,例: int max(int x,int y) int z ; z = x y ? x :y ; return(z);,int表示函数值为整型,即返回的值是整型,两个形式参数: 调用时,主调函数把实际参数的值传递给被调函数的形式参数x,y,主调函数的实际参数可以是任意的变量名或整型数或表达式,形参的意义是只代表:在被调函数中将有两个被传递过来的值,形参可以理解为未被赋值的变量,直到主调函数把实参传来后,才相当于为此变量赋了值,16,函数体:包括声明和语句两部分,声明部分: 1)定义

6、在函数体内要使用的变量; 2)对将要调用的函数作声明,语句部分: 是用来求函数的值,例: int max(int x,int y) int z ; z = x y ? X :y ; return(z);,把 z 的值作为函数值返回到主调函数中,函数头部:包括类型、名字和形参三部分,17,3. 可以有空函数 类型标识符 函数名( ),调用此函数时,什么也不执行,18,4. 对形参的声明方式 不同版本的C语言可能要求不同。 推荐使用: int max(int x,int y,19,函数调用的一般形式,函数名(实参列表,6.3.1 函数调用的一般形式,6.3 调用函数,20,说明: (1)调用无参函

7、数时,可以无实参名,但不能没有括号 (2)调用有参函数时,若实参个数多于1个,则各参数间用逗号分开。 (3)实参应与形参个数相等,最好是类型一致。 (4)实参与形参按顺序对应,一一传递数据。 (5)注意对实参的求值顺序,有自左向右或自右向左,21,例: void main( ) int f(int a,int b); int i=2,p; p=f( i ,+i ); printf(“%d”,p);,运行结果为-1或0,int f(int a,int b) int c; if (ab) c=1; else if(a=b) c=0; else c= - 1; return(c);,是函数本身存在对

8、参数的求值顺序问题; 同样,若函数中的参数是某种表达式,也有求值顺序问题,22,从函数出现在程序中的位置看,有3种函数调用方式: 函数语句 把函数作为一个语句。 例如: printstar() ; 一般作为语句时,只要求函数完成一定的操作即可,不要求返回值,函数调用的方式,23,2. 函数表达式 函数出现在一个表达式中,此时的表达式称为函数表达式。 例如: c=2*max(a,b) 函数要有返回值,以参加表达式的运算,24,3. 函数参数 函数调用作为一个实参。 例如: m=max(a,max(b,c,其中max(b,c)是一次函数调用,它的值又作为max另一次调用的实参,例如: printf

9、(“%d”,max(a,b); 函数调用作为函数的参数,实际上也是函数表达式调用的另一种形式,25,1.形式参数和实际参数,形式参数: 定义函数时,函数名后括号内的“变量名”称为形式参数; 实际参数: 主调函数中函数名后括号内的“参数”称为实际参数,6.2.2 函数参数与返回值数据传递,26,2. 实参和形参间的数据传递 在调用函数过程中,系统会把实参的值传递给被调用函数的形参,或者说,形参从实参得到一个值 该值在函数被调用期间有效,可以参加被调函数中的运算,27,例6.1 求和。 解题思路: (1)函数名应是见名知意,如:add (2)由于给定的两个数是整数,返回主调函数的 值(即和)应该是

10、整型 (3)add函数应当有两个参数,以便从主函数接收 两个整数,因此参数的类型应当是整型,28,函数调用过程中,两个函数会发生数据联系,include void main() int a,b,c; scanf(“%d%d”, c = add(a,b); printf(“%d”,c,add(int x,int y) int z ; z = x + y ; return(z,int add(int x,int y,29,关于形参和实参的5点说明: 1)实参可以是常量、变量或表达式, 但实参必须有确定的值。 在调用时把实参的值传给形参。 2)形参是在定义函数时指定的。 函数未被调用前,不占内存单元

11、。 调用结束后,所占单元立即被释放,30,3)形参必须指定类型。 (建议使用上述例子中的方式) 4)实参应与形参的类型相同或赋值兼容,类型转换时:向形参的类型转换。 实参类型低于形参类型,按2.6.4的规则转换。 实参类型高于形参类型,则强制向形参类型转换,31,例:实参类型高于形参 max(int x,int y) int z; printf(%d %dn,x,y); z= x y ? x+1 : y+1 ; return(z);,void main( ) float w; float a=9.5,b=7.3; w=max(a,b); printf(%f%f%f,w,a,b);,32,5)实

12、参对形参进行的是“值”传递。 即只传参数的值,不传参数的名。 传递是单向的,不能回传。 形参的值发生改变,不会改变主调函数的实参的值,33,void swap(int x,int y) int temp; temp=x;x=y;y=temp;,int main() int i=2,j=3; printf(“i=%d,j=%d”,i,j); swap(i,j); printf(“i=%d,j=%d”,i,j,交换前:i=2,j=3,交换后:i=2,j=3,例6.2 交换变量的值,34,通过函数调用使主调函数得到一个确定的值, 这就是函数的返回值,函数的返回值,35,说明: (1)函数的返回值是通

13、过函数中的return 语句获得的,1)不需要返回值时,可以没有renturn语句。 2)return z 与 return(z) 作用相同。 3)return 后面的值可以是表达式,36,2)函数值的类型 既然函数有返回值,此值必属于某一个确定的类型,1)应当在定义函数时指定函数值的类型; 2)省缺时认为是整型。 3)定义函数的类型应尽量与return语句的表达式类型一致,int add(float x,float y,37,3)若函数值的类型与return的表达式的类型不同,则,1)以函数的类型为准。即由函数的类型决定返回值的类型; 2)对数值型数据,可自动进行类型转换,int add(f

14、loat x,float y) float z; z=x+y; return(z);,38,例6.3 返回值类型与函数类型不同。 void main( ) int max(float x,float y); float a=7.3,b=9.5; int c; c=max(a,b); printf(“%d”,c);,max(float x,float y) float z; z=xy?x:y; return(z);,高类型向低类型转换,输出9,39,4)被调函数中没有return 语句也是合法的。 函数同样可以带回返回值,只是此值是不确定的。 使用void(无类型或空类型)确保函数不带回任何值,

15、40,例如: void main( ) int a,b,c; a=printstar(); printf(“n”); b=print_message(); printf(“n”); c=printstar(); printf(“n”); printf(“%d,%d,%d”,a,b,c);,printstar() printf(“* * * * * * * *”);,print_message() printf(“How do you do!”);,无return语句,41,5)若想明确要求不带回返回值,使用void把函数定义为“无类型”(或称空类型)。 例如:void printstar()

16、void print_message(),不允许再使用: a=printstar(); b=print_message() 此时禁止使用返回值,42,6.3.2 对被调用函数的声明和函数原型 在一个函数中调用另一个函数,需要具备以下3个条件,43,1)被调用函数必须是一个已经存在的函数。 (2)使用库函数时,应在文件头加: #include 命令。 常用的有输入输出库函数:stdio.h 数学库函数 :math.h 字符处理函数 : string.h (3)使用用户自定义的函数时,一般应在主调函数中对被调函数做声明,44,所谓声明: 就是向编译系统说明将要调用此函数,并把有关信息通知编译系统。

17、 信息包括: 函数名、函数类型、形参类型、形参个数、形参顺序,45,声明方法一: 在主调函数的声明部分进行声明,float add(float x,float y) float z; z=x+y; return(z,对函数的声明,例: 对被调用的函数作声明 void main() float a,b,c; scanf(“%f,%f,对函数的定义,float add(float x,float y),46,对函数的定义: 是指对函数功能的确立。 包括:函数名、函数的类型、形参及类型、函数体。 对函数的声明: 其作用是把函数名、函数类型以及形参类型、个数和顺序通知编译系统,使之在调用时按此对照检查

18、,47,当没有声明时: 由于编译是从上到下逐行进行的。当编译进行到调用函数的语句时,编译系统无法知道add是否为函数名,也无法判断实参的类型和个数是否正确。从而无法进行正确性检查,48,函数原型: 函数的声明称为函数原型。 一般形式为: 函数类型 函数名(参数类型1,参数类型2,); 例如: float add(float,float,49,声明的形式: 显式声明: 1)声明时应保证函数原型与函数首部写法一致。 2)调用时,函数名、实参个数和顺序与原型相同。 3)实参类型与原型中的形参类型赋值兼容,50,隐式声明: 1)编译系统把第一次遇到的该函数的形式作为声明看待,并默认为int型。(在VC

19、中不允许) 2)把被调函数写在主调函数前面,可以不加声明,float add(float x,float y) float z; z=x+y; return(z,void main() float a,b,c; scanf(“%f,%f”,51,声明方法二: 在函数外作声明: 在函数外部作了函数声明后,各主调函数中可不再作声明,52,char letter(char,char); float f(float,float); int i(float,float,char letter(char c1,char c2),float f(float x,float y),int i(float j,

20、float k),void main(),53,54,6.4.1 函数的嵌套调用 关于嵌套定义的概念: 在定义一个函数时,其函数体内又包含了另一个完整的函数定义,这个内嵌的函数只能被包含它的函数调用,其他函数不能调用,6.4 函数的嵌套调用和递归调用,55,C语言不能嵌套定义函数,但可以嵌套调用函数。 下图表示了两层嵌套调用,56,在被调用的函数中又调用了其它函数,57,例:输入4个整数,找出其中最大的数。 用函数的嵌套调用来处理,解题思路: main中调用max4函数,找4个数中最大者 max4中再调用max2,找两个数中的大者(标杆) max4中多次调用max2,可找4个数中的大者,然后把

21、它作为函数值返回main函数 main函数中输出结果,58,include int main() int max4(int a,int b,int c,int d); int a,b,c,d,max; printf(“4 interger numbers:); scanf(%d%d%d%d,主函数,对max4 函数声明,59,int max4(int a,int b,int c,int d) int max2(int a,int b); int m; m=max2(a,b); m=max2(m,c); m=max2(m,d); return(m);,max4函数,对max2 函数声明,m作为标

22、杆,60,int max2(int a,int b) if(a=b) return a; else return b;,找a,b中较大者的max2函数,return(ab)?a:b,61,int max4(int a,int b,int c,int d) int max2(int a,int b); int m; m=max2(a,b); m=max2(m,c); m=max2(m,d); return(m);,max4函数,m=max2(max2(a,b),c,int max2(int a,int b) return(ab?a:b);,62,int max4(int a,int b,int

23、c,int d) int max2(int a,int b); int m; m=max2(a,b); m=max2(m,c); m=max2(m,d); return(m);,max4函数,m=max2(max2(max2(a,b),c),d,63,int max4(int a,int b,int c,int d) int max2(int a,int b); int m; m=max2(a,b); m=max2(m,c); m=max2(m,d); return(m);,max4函数,ruturn max2(max2(max2(a,b),c),d,64,int max4(int a,int

24、 b,int c,int d) int max2(int a,int b); ruturn max2(max2(max2(a,b),c),d);,int max2(int a,int b) return(ab?a:b);,include int main() max=max4(a,b,c,d);,65,6.4.2 函数的递归调用,递归的定义: 在调用一个函数的过程中,又直接或间接地调用了该函数本身,66,定义一个函数f(x,例如: int f(int x) int y,z ; z=f(y) ; return(2*z) ;,在本函数中又调用了本函数,67,直接调用,间接调用,68,注意: 1)递

25、归调用应为有限次数的、可终止的。 2)用 if 语句控制在某一条件成立时继续执行递归调用,否则不再继续,69,例6.7 用递归调用方法求 n! 可用两种方法求n! 递推方法: 从一个已知的事实出发,按一定规律推出下一个事实,再从这个新的已知事实出发,向下再推出一个新的事实,即从1开始,乘2,再乘3一直乘到 n,70,2)递归方法: 分为回推和递推两个阶段,即:5!= 5x4! 而 4!= 4x3! 1!= 1 可用递归公式表示为: 1 (n=0,1) n!= n*(n-1)! (n1,71,include void main() int n; float y; scanf(“%d”,72,fl

26、oat fac(int n) float f; if(n0) printf(“n0,错误”);f= -1; else if(n=0|n=1) f=1; else f=n*fac(n-1); return(f);,73,f=fac(5-1)*n f=fac(4-1)*n f=fac(3-1)*n f=fac(2-1)*n f=fac(1,f=1*2*3*4*5 f=1*2*3*4 f=1*2*3 f=1*2 f=1,74,例: 递归调用,第一阶段(回推) 由第n 人 一直推到 第1人,第二阶段(递推) 由第 1 人一直推到第 n 人,age(5)=age(4)+2 age(4)=age(3)+2

27、 age(3)=age(2)+2 age(2)=age(1)+2 age(1)=10,75,76,可用模型表述为: age(n)=10 (n=1) age(n-1)+2 (n1,将第n个人的年龄表示为第(n-1)人年龄的函数,77,void main() printf(“%d”,age(5);,用一个函数来描述递归过程: age(int n) int c ; if(n=1) c=10 ; else c=age(n-1)+2 ; return(c),78,例6.8 (略) 例:输出Fibonacci数列第n项的值。 F1 =1 (n=1) F2 =1 (n=2) Fn =Fn-1 + Fn-2

28、(n3) 从公式的第三行我们可以直观地看出Fibonacci数列的递归性,79,include void main() unsigned long fib(int n); int n; scanf(%d,unsigned long fib(int n) if(n=1) return n; else return fib(n-1)+fib(n-2);,运行输入:5 运行结果:5,80,程序分析函数fib的执行过程: fib(5)发现5不小于等于1,转去执行fib(n-1)+fib(n-2)。可表示为:fib(5-1)+fib(5-2);语句中首先调用fib(5-1)。注意,此时不调用fib(5-

29、2,unsigned long fib(int n) if(n=1) return n; else return fib(n-1)+fib(n-2);,重复上述过程直至fib(1)发现1等于1,于是程序执行return n语句,并把n的值1返回到调用fib(1)处,替代fib(2-1),使得return语句的表达式变成1+fib(2-2)+的形式,显然此时应执行函数调用fib(2-2),其返回值为0,使得return语句的表达式变成1+0+。继续执行省略号()部分直至结束,fib(4)也会发现4不小于等于1,所以又在fib(4-1)+fib(4-2)语句中调用fib(3)。同样,此时亦不调用f

30、ib(4-2),当然也不去调用上次的fib(5-2,81,上述函数调用过程可表示为如下分解步骤: fib(5-1)+ fib(5-2) fib(4-1)+ fib(4-2)+ fib(5-2) fib(3-1)+ fib(3-2)+fib(4-2)+ fib(5-2) fib(2-1)+fib(2-2)+fib(3-2)+fib(4-2)+ fib(5-2) 由于fib(2-1)就是fib(1),而fib(1)的值是1,fib(0)的值为0,所以上式可以写成: 1 + 0 + 1 +fib(2-1)+fib(2-2)+fib(5-2) 1 + 0 + 1 + 1 + 0 +fib(3-1)+

31、fib(3-2) 1 + 0 + 1 + 1 + 0 +fib(2-1)+fib(2-2)+fib(3-2) 1 + 0 + 1 + 1 + 0 + 1 + 0 + 1 上面过程的最后一行的和是5,即Fibonacci数列第5项的值,unsigned long fib(int n) if(n=1) return n; else return fib(n-1)+fib(n-2);,82,可以看出,上述过程是逐步分解然后再逐步求值的过程。 逐步分解的过程是把函数从顶层fib(5)向底层fib(1)逐层调用,直到fib(1)为止。然后是逐步返回函数值,即从fib(1)的出口值逐步得到fib(5)的值

32、。 我们把这样的函数调用方法称为递归调用,并把逐步分解的过程称为“回推”,把逐步得到返回值的过程称为“递推”。 对于数学中的归纳问题,在C程序中可以用递归函数来求解。使用递归函数可以使程序结构简单,但是递归深度太大会使系统开销成倍增长。另外,递归深度太大也不利于程序员把握程序的执行过程。 大多数可以用递归方法求解的问题也可以用循环的方法求解。显然,在循环方法中,循环不能无限制地进行下去; 在递归方法中,回推的过程也不应无限制地进行下去,这两种方法都必须考虑设置程序控制结构的出口条件,如例中的if(n=1)就是出口条件,83,例: Hanoi(汉诺)塔问题。 塔内有3个座A、B、C,开始时座上有

33、64个盘子,盘子大小不等,大的在下,小的在上。 想把这64个盘子从座移到座,但规定每次只允许移动一个盘,且在移动过程中在3个座上都始终保持大盘在下,小盘在上。 在移动过程中可以利用B座。要求编程序输出移动一盘子的步骤,84,85,解题思路: 要把64个盘子从A座移动到C座,需要移动大约264 次盘子。一般人是不可能直接确定移动盘子的每一个具体步骤的。 老和尚会这样想:假如有另外一个和尚能有办法将上面63个盘子从一个座移到另一座。那么,问题就解决了。此时老和尚只需这样做,86,解题思路: (1) 命令第2个和尚将63个盘子从A座移到B座 (2) 自己将1个盘子(最底下的、最大的盘子) 从A座移到

34、C座 (3) 再命令第2个和尚将63个盘子从B座移到C座,87,A,B,C,将63个从A到B,第1个和尚的做法,88,A,B,C,将63个从A到B,第1个和尚的做法,89,A,B,C,将1个从A到C,第1个和尚的做法,90,A,B,C,将1个从A到C,第1个和尚的做法,91,A,B,C,将63个从B到C,第1个和尚的做法,92,A,B,C,将63个从B到C,第1个和尚的做法,93,A,B,C,将62个从A到C,第2个和尚的做法,94,A,B,C,将62个从A到C,第2个和尚的做法,95,A,B,C,将1个从A到B,第2个和尚的做法,96,A,B,C,将1个从A到B,第2个和尚的做法,97,A,

35、B,C,将62个从C到B,第2个和尚的做法,98,A,B,C,将62个从C到B,第2个和尚的做法,99,第3个和尚的做法 第4个和尚的做法 第5个和尚的做法 第6个和尚的做法 第7个和尚的做法 第63个和尚的做法,第64个和尚仅做:将1个从A移到C,100,A,B,C,将3个盘子从A移到C的全过程,将2个盘子从A移到B,101,A,B,C,将3个盘子从A移到C的全过程,将2个盘子从A移到B,102,A,B,C,将1个盘子从A移到C,将3个盘子从A移到C的全过程,103,A,B,C,将3个盘子从A移到C的全过程,将1个盘子从A移到C,104,A,B,C,将3个盘子从A移到C的全过程,将2个盘子从

36、B移到C,105,A,B,C,将3个盘子从A移到C的全过程,将2个盘子从B移到C,106,A,B,C,将2个盘子从A移到B的过程,将1个盘子从A移到C,107,A,B,C,将2个盘子从A移到B的过程,将1个盘子从A移到C,108,A,B,C,将2个盘子从A移到B的过程,将1个盘子从A移到B,109,A,B,C,将2个盘子从A移到B的过程,将1个盘子从A移到B,110,A,B,C,将2个盘子从A移到B的过程,将1个盘子从C移到B,111,A,B,C,将2个盘子从A移到B的过程,将1个盘子从C移到B,112,A,B,C,将2个盘子从B移到C的过程,113,A,B,C,将2个盘子从B移到C的过程,1

37、14,A,B,C,将2个盘子从B移到C的过程,115,A,B,C,将2个盘子从B移到C的过程,116,由上面的分析可知:将n个盘子从A座移到C座可以分解为以下3个步骤: (1) 将A上n-1个盘借助C座先移到B座上 (2) 把A座上剩下的一个盘移到C座上 (3) 将n-1个盘从B座借助于座移到C座上,117,可以将第(1)步和第(3)步表示为: 将“one”上n-1个盘移到“two”(借助“three”)。 在第(1)步和第(3)步中,one 、two、three和A、B、C的对应关系不同。 对第(1)步,对应关系是one对应A,two对应B,three对应C。 对第(3)步,对应关系是one

38、对应B,two对应C,three对应A,118,把上面3个步骤分成两类操作: (1) 将n-1个盘从一个座移到另一个座上(n1)。是大和尚让小和尚做的工作,它是一个递归的过程,即和尚将任务层层下放,直到第64个和尚为止。 (2) 将1个盘子从一个座上移到另一座上。这是大和尚自己做的工作,119,编写程序 用hanoi函数实现第1类操作(模拟小和尚的任务) 用move函数实现第2类操作(模拟大和尚自己移盘) 函数调用hanoi(n,one,two,three)表示将n个盘子从“one”座移到“three”座的过程(借助“two”座) 函数调用move(x,y)表示将1个盘子从x座移到y座的过程。

39、x和y是代表A、B、C座之一,根据每次不同情况分别取A、B、C代入,120,include void main() void hanoi(int n,char one, char two,char three); int m; printf(“the number of diskes:); scanf(%d,121,void hanoi(int n,char one,char two, char three) void move(char x,char y); if(n=1) move(one,three); else hanoi(n-1,one,three,two); move(one,th

40、ree); hanoi(n-1,two,one,three);,122,void move(char x,char y) printf(%c-%cn,x,y);,123,6.5.1 数组元素作函数实参,由于数组元素可以是表达式的组成部分,因此元素可以作为实参,采用“值传送”,即单向传递方式“单向值传递,6.5 用数组做函数参数,124,解题思路: 定义数组a,用来存放10个数 设计函数max,用来求两个数中的大者 在主函数中定义变量m,初值为a0,每次调用max函数后的返回值存放在m中 用标杆法,依次将数组元素a1到a9与m比较,最后得到的m值就是10个数中的最大者,例6.9(略) 例:输入1

41、0个数,要求输出其中值最大的元素和该数是第几个数,125,include int main() int max(int x,int y); int a10,m,n,i; for(i=0;im) m=max(m,ai); n=i; printf(“largest is %dn,m); printf(“%dn”,n+1,定义形参时不能使用元素形式ai,int max(int x,int y) return(xy?x:y);,数组元素作为实参 形参仍为变量,126,6.5.2 用一维数组名作函数参数,除了可以用数组元素作为函数(实)参数外,还可以用数组名作函数参数(包括实参和形参) 用数组元素作实参

42、时,向形参变量传递的是数组元素的值 用数组名作函数实参时,向形参传递的是数组首元素的地址首地址,127,实参与形参都用数组名,例:用个一维数组存放10个学生的成绩, 求平均成绩。 void main() float average(float array10); float score10 ; int i ,aver ; for(i=0;i10;i+) scanf(“%f”,数组名,数组名,128,float average(float array10) int i ; float aver , sum=array0 ; for(i=1 ; i10 ; i+) sum=sum+array i

43、; aver=sum/10 ; return(aver),形式参数名,可任意,第个元素的值已经赋给sum,因此 i 从开始,129,说明: )用数组名作函数参数,应在主调函数和被调函数中分别定义数组,例如array是形参数组名,score是实参数组名,分别在其所在函数中被定义,不能只在一方定义。 )实参数组与形参数组类型应一致。 )只把实参数组的首地址传给形参数组,因此scoren和arrayn指的是同一单元。 )不指定形参数组大小时,可在调用时另传一个参数来指定要处理数组元素的个数,130,重要说明: 由于不是把数组的值传给形参,而是把实参数组的首地址传给形参数组; 因此:两个数组就共同占有

44、同一段内存单元; 显然:形参数组中各元素的值如果发生变化, 也会使实参数组元素的值同时发生变化, 这与用变量作函数参数时不同; 这一特点可产生好的和坏的两方面的结果,131,int main() int a2=1,2; swap(a); printf(“a0=%dna1=%dn”,a0,a1); return 0;,例6.10 交换元素的值。 #include void swap(int x) int z; z=x0;x0=x1;x1=z;,132,例6.11和12 有两个班级,分别有35名和30名学生,调用一个average函数,分别求这两个班的学生的平均成绩,解题思路: 需要解决怎样用同一

45、个函数求两个不同长度的数组的平均值的问题 定义average函数时不指定数组的长度,在形参表中增加一个整型变量i 从主函数把数组实际长度从实参传递给形参i 这个i用来在average函数中控制循环的次数 为简化,设两个班的学生数分别为5和10,133,include int main() float average(float array ,int n); float score15=98.5,97,91.5,60,55; float score210=67.5,89.5,99,69.5, 77,89.5,76.5,54,60,99.5; printf(“%6.2fn”,average(sco

46、re1,5); printf(“%6.2fn”,average(score2,10); return 0;,134,float average(float array ,int n) int i; float aver,sum=array0; for(i=1;in;i+) sum=sum+arrayi; aver=sum/n; return(aver);,调用形式为average(score1,5)时,相当于score10,相当于score1i,相当于5,135,float average(float array ,int n) int i; float aver,sum=array0; fo

47、r(i=1;in;i+) sum=sum+arrayi; aver=sum/n; return(aver);,调用形式为average(score2,10)时,相当于score20,相当于score2i,相当于10,136,例6.13 用选择法对数组中10个整数按由小到大排序,解题思路: 即先将10个数中最小的数与a0对换; 再将a1到a9中最小的数与a1对换 每比较一轮,找出一个未经排序的数中最小的一个 共比较9轮,137,a0 a1 a2 a3 a4,3 6 1 9 4,1 6 3 9 4,1 3 6 9 4,1 3 4 9 6,1 3 4 6 9,小到大排序,138,include in

48、t main() void sort(int array,int n); int a10,i; printf(enter array:n); for(i=0;i10;i+) scanf(%d,139,void sort(int array,int n) int i,j,k,t; for(i=0;in-1;i+) k=i; for(j=i+1;jn;j+) if(arrayjarrayk) k=j; if(k!=i) t=arrayk;arrayk=arrayi; arrayi=t;,K用来记住最小数的下标,i是轮次,j是轮内次数,140,在被调用函数中对形参数组定义时可以省略第一维的大小,6.

49、5.3 多维数组名作函数参数,多维数组元素作实参, 形参只能是变量,141,例6.14 有一个的矩阵,求所有元素中的最大值,解题思路: 采用标杆法,142,多维数组名作实参,void main() max_value(int array 4); int a34=1,3,5,7,2,4,6,8,15,17,34,12 ; printf(“max value is %dn” , max_value(a) ;,143,max_value(int array 4) int i ,j max ; max=array00; for(i=0 ; imax) max=array i j ; return(ma

50、x) ;,定义形参数组,可不指定第一维的大小,若在此函数的任何位置改变形参函数元素的值,都相当于改变了实参函数元素的值,若取a55元素的值,是合法的,144,6.6.1 局部变量,定义变量可能有三种情况: 在函数的开头定义 在函数内的复合语句内定义 在函数的外部定义,6.6 局部变量和全局变量,145,在一个函数内部定义的变量只在本函数范围内有效 在复合语句内定义的变量只在本复合语句范围内有效 在函数内部或复合语句内部定义的变量称为“局部变量,146,例如: float f1 ( int a ) int b , c ;,char f2( int x,int y) int a , b ;,voi

51、d main() int a ,b ; 注意:a , b 也只在主函数中有效,147,说明: 1)main中定义的变量(如 m , n )也只在主函数中有效; 2)不同函数中可以使用相同名字的变量,但在内存中占据不同的单元; 3)形参也是局部变量; 4)在一个函数内部,可以在复合语句中定义变量,这些变量也只在本复合语句中有效,void main() int a , b ; int c ; c = a + b ;,148,6.6.2 全局变量,在函数之外定义的变量称为外部变量; 外部变量是全局变量(全程变量,外部变量可为本文件中其他函数所共用 它的有效范围是从定义变量的位置到本源文件结束,149

52、,int p=1,q=5,void main() int m ,n ;,其作用范围:从定义处开始,一直到本源程序结束为止,其作用范围:从定义处开始,一直到本源程序结束为止,既不会对其前面的程序(如f1函数)有作用,float f1(int a ) int b,c ;,char c1 , c2,char f2(int x ,int y ) int i ,j ;,例如,150,说明: 1)全局变量增加了函数间数据联系的渠道; 由于同一文件中的所有函数都可引用全局变量,如果在某个函数中改变了全局变量的值,就能影响到其他函数,这相当于各函数间有直接的传递通道。 另外,由于函数只有一个返回值,全局变量的

53、上述作用使函数得到一个以上的返回值,151,例6.15 使用全局变量得到多于一个返回值,float Max = 0 , Min = 0 ; float average(float array ,int n) int i ; float aver ,sum=array0; Max=Min=array0; for(i=1;iMax) Max=array i ; else if(array i Min) Min=array i ; sum=sum+array i ; aver=sum/n ; return(aver) ;,152,void main() float ave ,score10; int

54、 i ; for(i=0;i10;i+) scanf(“%f”,Max和Min的值可供各函数使用。 利用全局变量可减少实参与形参的个数,从而减少空间和传递时间,153,说明: 2)只在必要时使用全局变量,原因是: 全局变量在程序的全部执行过程中都占用存储单元; 它使函数的通用性降低; 难于判断每个瞬时各个外部变量的值,3)如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用,154,例6.16 外部变量与局部变量同名,int max(int a , int b) int c ; c=ab ? a : b ; return ( c ) ;,vo

55、id main() int a = 8 ; printf(“%d” ,max(a , b ) ) ;,int a=3 , b=5,155,6.7 变量的存储类别,156,6.7.1 动态存储方式和静态存储方式,静态存储方式是指: 在程序运行期间分配固定的存储空间的方式; 动态存储方式是指: 在程序运行期间根据需要动态地分配存储空间方式,从变量的作用域(空间)角度可分为全局变量和局部变量; 从变量值存在的时间(生存期)角度可分为静态存储方式和动态存储方式,157,存储空间的分配情况: 1)程序区 2)数据区 静态存储区:存放全局变量。 程序开始执行时就分配存储区, 程序执行完毕才释放,158,动

56、态存储区:存放形式参数 自动变量 函数调用时的现场保护和返回地址等 在函数调用开始时才分配动态存储空间, 函数结束时释放。 在程序执行过程中,这些分配和释放是动态的,159,说明: 1)如果在一个程序中两次调用同一函数, 每次分配给此函数中局部变量的存储空间地址 可能是不同的; 2)如果一个程序包含若干个函数, 每个函数中的局部变量的生存期并不等于整个 程序的执行周期,160,每个变量和函数都有两个属性,动态方式,数据类型,存储类别,auto 类 register 类 static 类 外部变量(extern,静态方式,构造 基本 指针 空,整型 实型 字符,161,根据变量的存储类别, 可以

57、知道变量的作用域和生存期,162,1.自动变量(auto变量) 未被声明为static存储类别的局部变量都是动态存储类,称此类局部变量为自动变量。 用关键词auto作自动变量存储类别的声明,例:int f ( int a ) auto int b , c = 3 ;,6.7.2 局部变量的存储类别,163,2.静态局部变量(static局部变量) 用关键词static作声明指定局部变量为“静态局部变量”。 目的是保留该局部变量的值,164,例6.17 利用静态局部变量的值 f ( int a ) auto int b = 0 ; static int c = 3 ; b=b+1 ; c=c+1

58、 ; return ( a+b+c) ;,void main() int a=2 , i ; for( i=0;i3;i+) printf(“%d”,f(a) ;,165,对静态局部变量的说明: 1)静态局部变量属静态存储类别,在静态存储区分配单元,在整个程序运行期间都不释放。 在这一点上类似于全局变量,但它又仅对本函数起作用,对其他函数不起作用,166,f(int a) auto int b = 0 ; static int c = 3 ; b=b+1 ; c=c+1 ; return ( a+b+c) ;,void main() int a=2 , i ; for( i=0;i3;i+)

59、printf(“%d%d”,f(a),g(a);,g(int a) auto int b=0 , c=0; b=b+1 ; c=c+1 ; return(a*b*c);,此函数的c必须定义并赋初值 本函数的返回值一直是2,167,对静态局部变量的说明: 2)对静态局部变量是在编译时赋初值的,即只赋值一次,程序运行时它已有初值,以后调用函数时不再重新赋初值,而是保留使用上次函数调用结束时的值。但其他函数不能引用它。 对于自动变量,则不在编译时赋初值,而是在函数调用时进行赋值,每调用一次函数,重新赋初值一次,若对静态变量在定义时不赋值则编译时自动赋0或空字符,对自动变量在定义时若不赋值则其值是不确

60、定的,168,例:输出1到5的阶乘值。 解题思路: 递推法 可以编一个函数用来进行连乘,如第1次调用时进行1乘1,第2次调用时再乘以2,第3次调用时再乘以3,依此规律进行下去,169,include int main() int fac(int n); int i; for(i=1;i=5;i+) printf(“%d!=%dn”,i,fac(i); return 0; int fac(int n) static int f=1; f=f*n; return(f);,若非必要,不要多用静态局部变量,170,3. 寄存器变量(register变量) 使用寄存器变量的目的是提高速度。 例: 使用寄

温馨提示

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

评论

0/150

提交评论