版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第5章 函数学习目标与要求1、掌握C语言函数的定义2、掌握C程序调用函数的方法3、了解递归函数的用法4、掌握宏定义和文件包含的使用方法 本章要点函数概要 函数的定义 函数的参数和函数的值 函数的调用 函数的嵌套调用 函数的递归调用数组作为函数参数 局部变量和全局变量 变量的存储类别 预处理命令5.1 函数概要 一个C程序可由一个主函数和若干个函数构成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。一个C程序只能有一个也必须有一个main函数,而子函数的数目实际上是不限的。 mainabcdefghhieg示意图函数的分类:按用户使用分类:(1)标准
2、(2)自定义函数按函数形式分类:(1)无参函数 (2)有参函数5.1 函数概要 5.2 函数的定义无参函数的定义形式类型标识符 函数名( ) 声明部分语句有参函数定义的形式类型标识符 函数名(形式参数表列) 声明部分 语句空函数dummy()int max(int x,int y)int z; z=xy?x:y;return(z);5.3 函数参数和函数的值形式参数和实际参数形参:在定义函数时,函数名后面括号中的变量称为形式参数实参:在调用函数时,函数名后面括号中的参数(可以是表达式)称为实际参数。说明:例5-1函数的返回值 返回值的方式:通过return语句返回。可以有一条返回语句,也可以有
3、多条返回语句,也可以没有返回语句。 return 语句的一般形式为:return (表达式) 返回值的类型一般应和函数类型一致,如不一致,则以函数型为准。即函数类型决定返回值的类型。 为了明确表示不带回值,可以用void定义无类型。5.3 函数参数和函数的值例5-2main()int a,b,c;scanf(%d,%d,&a,&b);c=max(a,b);printf(Max is %d, c);int max(int x, int y)int z;x=x+2;y=y+4;z=xy?x:y;return(z);程序运行情况输入:7,8结果:Max is 12例5-1 调用函数时的数据传递调用结
4、束后调用时b8y877axb8y1297axint max(float x, float y)float z; /* z为实型变量*/z=xy?x:y;return(z);main()float a,b; int c;scanf(%f,%f,&a,&b);c=max(a,b);printf(Max is %d, c);程序运行情况:1.5,2.5Max is 2例5-2返回值类型与函数类型不同 5.4 函数的调用函数调用的一般形式 函数名(实参表列);函数调用的方式 1)函数语句 如:printstar(); 2)函数表达式 如: c=2*max(a,b); 3)函数参数 如: m=max(a
5、,max(b,c);例5-3例5-4对被调用函数的声明和函数原型 函数声明是对所用到的函数的特征进行必要的声明,编译系统以函数声明中给出的信息为依据,对调用实际参数进行控制,以保证调用表达式与函数之间的参数正确传递。函数声明的格式:类型标识符 函数名(类型标识符 形参, 类型标识符 形参,);在下列情况下可以缺省函数声明;(1)被调函数出现在主调函数之前(2)函数返回值为int类型(3)在所有函数定义之前,在函数的外部已做了函数声明。 5.4 函数的调用int f(int a,int b) /*函数定义*/int c;if(ab) c=1;else if(a=b) c=0;else c=-1;
6、return(c);main()int i=3,p,j=2;p=f(i,+j); /*函数调用*/printf(%dn,p);程序运行结果:0例 5-3函数调用和函数定义+j3b333iamain()float add(float x,float y);float a,b,c;scanf(%f,%f,&a,&b);c=add(a,b);printf(sum is %f,c);float add(float x,float y)float z;z=x+y;return(z);程序运行情况:输入3.6, 6.5结果:sum is 10.100000例 5-4 对被调用的函数作声明5.5 函数的嵌套
7、调用C语言不能嵌套定义,即在定义一个函数时,其函数体内不能包含另一个函数的定义。但可以嵌套调用函数,在调用一个函数的过程中又调用另一个函数。例5-5例5-5 函数的嵌套调用。main( ) int n=3; printf (%dn,sub1(n); int sub1(int n) int i,a=0; for (i=n; i0; i-) a+=sub2(i); return a ; int sub2(int n) return n+1; 程序输出结果:9 5.6 函数的递归调用递归的基本概念递归调用:在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。递归函数:在函数体
8、内调用该函数本身。例如:直接调用sub函数本身int sub(int x) int y,z; if( ) z=sub(y); else return ;数值型问题递归函数的编程方法对于数值型问题,首先要找出解题的数学公式,这个公式必须是递归定义的,且所处理的对象要有规律地递增或递减,然后确定递归结束条件。例5-6 5.6 函数的递归调用例5-6 编一递归函数求n! 思路:以求4的阶乘为例:4!=4*3!,3!=3*2!,2!=2*1!,1!=1,0!=1。递归公式:n! =1 (n=0, 1)n(n-1)! (n1)递归结束条件:当n=1或n=0时,n!=1。动画演示f (4)4*f (3)3
9、*f (2)2*f (1)2*13*2*14*3*2*124f (4)的执行过程程序示例有些问题不能直接用数学公式求解,非数值问题比数值问题更难找出递归算法,它不能用一个递归公式表示。首先要把问题由繁化简,由大化小,而某个小问题的解法与原问题解法相同,并且越来越简单直到有确定的解。例5-7 5.6 函数的递归调用非数值型问题递归函数的编程方法例5-7 编制一递归函数,将一个十进制正整数(如:15613)转换成八进制数形式输出。思路:十进制整数转换成八进制整数的方法是除8逆向取余。 余数: 商:15613%8=5 15613/8=19511951%8=7 1951/8=243243%8=3243
10、/8=3030%8=630/8=33%8=33/8=0结果:36375 该题实际上是要把一个十进制数除以8得到的余数逆向输出。就是先得到的余数后输出,最后得到的余数最先输出。我们先由大化小:求八进制数变成求一系列余数的问题。求第一个余数是将15613除以8取余,因为先得到的余数后输出,所以把这个余数存在一个变量m中,接下去求下一个余数。和求第一个余数的方法相同,只是被除数变成了15613除以8的整数商1951。因此,这是一个递归调用的问题。定义变量m存放余数,x存放被除数。 先求出余数m:m=x%8; 求x除以8取余后的整数商:x=x/8; 如果x不等于0,递归调用该函数,否则执行。 输出余数
11、m。 返回调用点。递归算法描述如下:例5-7 编制一递归函数,将一个十进制正整数(如:15613)转换成八进制数形式输出。#include stdio.hvoid dtoo(int x) int m; m=x%8; x=x/8; if (x!=0) dtoo(x); printf(%d,m);例5-7 编制一递归函数,将一个十进制正整数(如:15613)转换成八进制数形式输出。main( ) int n; scanf(%d,&n); printf(%d=(,n); dtoo(n); printf()8n);程序运行情况:1561315613=(36375)85.7 数组作为函数参数数组元素作函
12、数实参数组名作函数参数用多维数组名作函数参数例5-8例5-9说明例5-11例5-10有两个数组a,b,各有10个元素,将它们对应地逐个相比(即a0与b0比)。如果a数组中的元素大于b 数组中的相应元素的数目多于b数组中元素大于a数组中相应元素的数目(如aibi6次,biai3次,其中i每次为不同的值),则认为a数组大于b数组,并分别统计出两个数组相应元素大于、等于、小于的次数。例5-8 数组元素作为函数实参举例说明程序运行情况:enter array a:1 3 5 7 9 8 6 4 2 0enter array b:5 3 8 9 -1 -3 5 6 0 4aibi 4 timesai=b
13、i 1 timesaibi 5 timesarray a is smaller than array b例5-8 数组元素作为函数实参举例说明程序示例 数组名作为参数的说明:在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送是把实参变量的值赋予形参变量。在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。前面曾介绍过,数组名就是数组的首地址。因此在数组名作函数参数时,实际是把实参数组的首地址赋予形参数组名。形参数组名取得该首地
14、址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。 数组名作为参数的说明:上图说明了这种情形。图中设a为实参数组,类型为整型。a占有以2000为首地址的一块内存区。b为形参数组名。当发生函数调用时,进行地址传送,把实参数组a的首地址传送给形参数组名b,于是b也取得该地址2000。于是a,b两数组共同占有以2000为首地址的一段连续内存单元。从图中还可以看出a和b下标相同的元素实际上也占相同的两个内存单元(整型数组每个元素占二字节)。例如a0和b0都占用2000和2001单元,当然a0等于b0。类推则有ai等于bi。 例5-9 编程序,实现字符串连接。算
15、法思想:将s2字符串连接到s1字符串后。ABD0 s10 s11 s1i-1 s1is1字符串while (s1i!=0) i+;S2字符串EF0k=0;while (s2k!=0) s1i=s2k; i+; k+; s20 s21 例5-9 编程序,实现字符串连接。scat函数还可简化为:void scat(char str1 ,char str2 ) int i=0,k=0; while (str1i) i+; while (str1i+=str2k+);程序运行情况:Input s1:I am Input s2: teacher!Output s1:I am teacher!Output
16、 s2:teacher!程序示例用选择法对数组中十个整数进行由小到大排序。排序的算法如下: 1) 先从b0 b9的十个整数中找出最小的数,与元素b0交换。 2) 接下来在从b1 b9的九个整数中找出最小的数,与元素b1交换。依次类推,每一次比较,减少比较一个数,共比较九次,剩下最大的数留在b9。例5-10 数组名作函数参数举例说明3以5个整数的排序为例,说明选择法的比较过程:b0 b1 b2 b3 b4 3 6 1 9 4 未排序时的情况 1 6 3 9 4 将5个数中的最小数1与b0 互换 1 3 6 9 4 将4个数中的最小数3与b1 互换 1 3 4 9 6 将3个数中的最小数4与b2
17、互换 1 3 4 6 9 将2个数中的最小数6与b3 互换 剩下最大的数留在b4。例5-10 数组名作函数参数举例说明3void sort(int b ,int n) int i,j,k,t ; for(i=0;in-1;i+) k=i; for(j=i+1;jn;j+) if(bjbk) k=j; t=bk; bk=bi; bi=t; 例5-10数组名作函数参数举例说明3main( ) int a10,i; printf(enter the arrayn); for(i=0;i10;i+) scanf(%d,&ai); sort(a,10); printf(the sorted array:
18、 n); for(i=0;ib?a:b;return(c);main()int a=8; /a为局部变量*/printf(%d,max(a,b);形参a、b作用范围局部变量a作用范围全局变量b作用范围 程序运行情况:8例5-12 外部变量与内部变量同名举例 5-9 变量的存储类别动态存储方式和静态存储方式静态存储方式:全局变量,静态局部变量static动态存储方式:函数形参,自动变量,函数调用时的现场保护和返回地址。自动变量auto 变量函数中的局部变量没有用static声明,都是自动变量。调用该函数时系统给他们分配存储空间,函数调用结束时这些存储空间被自动释放。静态局部变量static变量函
19、数中的局部变量的值在函数调用结束后不消失而保留原值,即起占用的存储单元不释放,在下一次调用该函数时,该变量已有值,就是上一次函数调用结束时的值。例5-13f(int a) auto b=0;static c=3;*/函数f中静态局部变量c(static)*/ b=b+1;c=c+1;return a+b+c; main( ) int a=2; for(i=0;i3;i+) printf(%d ,f(a); 程序运行结果:7 8 9例5-13 静态局部变量的值函数调用过程中,各变量值的变化图示第几次调用调用时初值 调用结束时的值 b c b ca+b+c第一次第二次第三次 0 0 0 3 4 5
20、 1 1 1 4 5 6 7 8 9例5-13 静态局部变量的值5.10 预处理命令宏定义文件包含条件编译C语言中的编译预处理命令有:所有的预处理命令以符号#开头,并且每个预处理命令必须单独占一行,不使用分号作为结束符。宏定义 无参宏定义 定义的一般形式为: #define 标识符 字符串例如:#define PI 3.1415926带参宏定义带参宏定义的一般形式为#define 宏名(形参表) 字符串在字符串中含有各个形参。带参宏调用的一般形式为: 宏名(实参表); 如:#define S(a,b) a+b area=S(3,2)*5;例5-14例5-15#define R 3.0#defi
21、ne PI 3.1415926#define L 2*PI*R#define S PI*R*Rmain() printf(L=%fnS=%fn,L,S);程序运行情况:L=18.849556S=28.274333例5-14无参宏定义 说明:宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用# undef命令。在进行宏定义时,可以引用已定义的宏名,可以层层置换。#define PI 3.1415926#define CIRCLE(R,L,S,V) L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*Rmain()float r,l,s,v; scanf(%f,&r); CIRCLE(r,l,s,v); printf(r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2fn,r,l,s,v);程序运行情况:输入:3.5输出:r=3.50,l=21.99,s=38.48,v=179.59例5-15 带参宏定义文件包含 文件包含命令行的一般形式有两种:#include 文件名 例如#include s1
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论