版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1第2-4讲 自定义函数函数的根本知识函数的定义函数的参数函数的返回值函数的调用函数调用的一般形式调用的方式嵌套调用递归调用函数与数组局部变量和全局变量变量的动态存储方式和静态存储方式24.1 概述C语言函数分为两类:库函数scanf printf sqrt cos strcmp自定义函数main每个程序必须有一个main函数说明程序运行的起始点只用一个main编程,可能使程序太大、太复杂例4-1,输出如下图内容:34在面向过程的程序设计中,一个较为复杂的程序一般通过模块化,分解成主模块与假设干子模块的组合,即一个主函数与假设干子函数。“分的优点:便于自上而下的模块化编程;通过在适当的地方使用
2、函数,可以减短源程序的长度;更容易定位和隔离有错误的函数,便于进一步的检查;函数可以被其他多个程序使用。模块化程序设计可以把大型程序组织成小而独立的程序段模块,它们单独命名,是单个的可调用的程序单元。在C语言中,每个模块就是一个函数,负责完成单个任务。C语言程序一般都由许多小的函数组成。5模块化程序设计的特征:每个模块只做一件事情。模块之间的通信只允许通过调用模块来实现。某个模块只能被更高一级的模块调用。如果不存在调用与被调用关系,模块之间是不能直接通信的。所有模块都是使用控制结构设计成单入口、单出口的系统。6例4-2,将例4-1中重复执行的局部改写成函数/ 函数声明原型/ 函数调用/ 函数定
3、义函数体函数派生数据类型函数名标识符函数具有与之相关的类型使用之前,函数名及其类型必须已经声明和定义。7多函数程序函数就是含有执行某个特定任务的代码块。函数一旦设计和封装后,就可以看作是一个“黑盒子,它从主程序中获得一些数据,并返回一个值。函数操作的内部细节对程序的其他局部是不可见的。程序所知道的函数就是:输入什么数据以及输出什么数据。每个C程序至少包含一个函数,即main函数主函数。由主函数调用其它函数,其它函数也可以互相调用。同一个函数可以被一个或多个函数调用任意屡次。一个程序可以保存在一个或多个源文件中。各个文件可以单独编译,并可以与库中已编译过的函数一起加载。84.2 函数的根本知识一
4、、函数的定义 包括以下元素函数名函数类型参数列表局部变量声明函数语句返回语句函数头函数体9 函数定义的一般格式:函数类型 函数名形式参数列表/函数头,末尾没有; 局部变量声明; 语句1; 语句2; . return 语句;函数类型,即函数带回来的值的类型。缺省为int。如不返回任何值,那么函数类型应指定为void也是C的根本类型之一10形参,以接收从调用函数发送来的数据。形参列表包含了变量的声明,变量之间用逗号分隔开;一般格式为:type1 name1, type2 name2, , type n name n其中: type1 , type2 , type n 是类型标识符,表示形参的类型;
5、 name1, name2, , name n 是形参名。float quadratic(int a, int b, intc) .double power(double x, int n) .float mul(float x, float y) .int sum(int a, int b) .int sum(int a, b) . 错形参可以没有,即“无参函数。可以在参数列表的括号中使用关键字voidvoid PrintLine(void) .函数在没被调用时,形参只是一个符号。只有函数在被调用时,才由主调函数将实际参数实参赋予形参。11函数体,包含了函数声明及完成任务所需的语句。依次为:
6、1) 局部变量,即本函数所需的变量;2) 完成函数任务的语句;3) return 语句,返回函数所得的值。不返回函数值可以省略return语句,但要注意把函数的返回类型声明为void。函数体可以为空,即“空函数。此函数不作任何工作,没有任何实际作业。可先占位,再补充几种典型的例如:1213函数的类型和返回值函数的返回值类型应当属于某个确定的类型。如果在定义函数时不指定函数类型,系统会隐含指定函数类型为int,函数结束也需返回一个int型值。函数的返回值由 return 语句给出。 return表达式; 或 return 表达式;如果函数没有返回值,函数名前的类型标识符为void,return
7、语句可省略不写。 如果return中的值与函数值的类型不一致,那么以函数类型为准。即在返回时先作隐含的类型转换,然后再返回。/ 3.5 被转换成 3 后返回给主函数 14例4-3,编写一个函数用于获取三个整数的最大值。15二、函数的调用函数的调用一般格式: 函数名 实参列表; 实参列表中的参数应与函数原型中形参的个数相同、类型相符 一一对应。 主调函数向被调函数以值传递的方式传递。1617 函数调用的执行过程main( ) mul(10,5)调 mul(10,5) 返回 结束保存:返回地址当前现场恢复:主调程序现场返回地址18函数调用的方式 函数调用可以作为一条语句出现,这时函数可以没有返回值
8、。PrintLine();函数调用也可以出现在表达式中,这时必须有一个明确的返回值。a=mul(10,5);printf(%dn, mul(a, b) );If( mul(m, n) total ) printf(Large);函数不能用在赋值语句的左边。 mul(a, b) 15; 错 19函数的嵌套调用 C语言 不允许函数嵌套定义,即:在函数定义中再定义一个函数是非法的。C语言的函数定义都是互相平行、独立的。C语言可以嵌套调用函数,即在调用函数的过程中,又调用另一个函数。main( ) 结束 return returnfun1( )调fun1( )fun( )调fun( )20例4-4,用
9、函数实现21三、函数的声明函数原型 返回类型 函数名形式参数表;函数原型是一条程序语句,必须以分号结束。C程序中的所有函数在使用之前都必须声明,即先声明后使用。如果被调函数的定义出现在主调函数之前,可以不必声明。函数原型声明可在所有函数包括main之前。全局原型,该函数对程序中的所有函数都是可用的。函数原型声明位于某函数定义之中。局部原型,该函数主要是被包含它们的函数使用。标准库函数的函数原型都在头文件中提供,可用 # include 包含这些原型文件。 函数原型和函数定义在返回类型、函数名和参数表上必须完全一致。 函数原型的参数表可不必包含参数名称,而只要包含参数类型即可。例如: int m
10、ax(int, int,int); 等价于:int max(int a, int b,int c);22例4-5,随机函数rand 和srand的使用rand(),返回一个0RAND_MAX(32767)之间的随机整数。RAND_MAX和rand定义在头文件中计算机产生的是一个伪随机数,即这个随机数序列有一个长度,会出现重复用srand(int seed),其参数称为随机数序列种子。即不同的伪随机数序列种子,可以得到不同的伪随机数序列。一般采用系统时间作为随机数序列种子,例如: srand(unsigned int) time(NULL);23244.3 函数的类型根据是否有参数,是否有返回值
11、,可以将函数分为以下几种类型:类型1:无参数、无返回值的函数。类型2:有参数、无返回值的函数。类型3:有参数、有返回值的函数。类型4:无参数、有返回值的函数。25一、无参数、无返回值的函数当函数没有参数时,不用从调用函数接收任何数据。同样,它也不返回值,调用函数不会从被调用函数中接收任何数据。 function1() . . function2() . . function2() . . . . . 控制控制无输入无输出26例4-6,请编写一个含有多个函数的程序,这些函数之间不进行任何数据通信。2728二、有参数、无返回值的函数调用函数和被调用函数之间的数据通信情况: function1()
12、. . function2(a) . . function2(x) . . . . . 参数值无返回值29实参和形参在数量、类型和顺序上必须匹配。实参的值被逐个赋给形参 main() . function1(a1, a2, a3, ., am) . function1(f1, f2, f3, ., fn) . . 函数调用被函数调用实参形参30必须保证函数调用有匹配的参数。实参形参,多余的实参将被丢弃;实参形参,未匹配的形参将被初始化为垃圾值;数据类型的任何不匹配情况都将导致传递垃圾值。形参必须是有效的变量名,而实参可以是变量名、表达式或常量实参到形参的传递实现的是值的传递。被调函数中发生的一
13、切不会影响到实参中的变量。例4-7,修改例4-6,使得在调用函数中包含参数。 void PrintLine(char ch); void value(float p, float r, int n);313233三、有参数、有返回值的函数为确保程序间更高的可移植性,函数编码中往往不包含任何I/O操作。调用函数与被调用函数之间具有双向通信。 function1() . . function2(a) . . function2(x) . . . . return(b); 参数值函数结果34例4-8,修改例4-7中的value函数,使其能将计算的sum值返回给main函数,由main函数按要求实现输
14、出。同时扩展PrintLine函数,可将显示字符的长度作为参数传递。3536四、无参数但有一个返回值的函数例4-9,设计一个与getchar 类似的函数来获取一个整数。374.4 函数调用时参数的传递在函数未被调用时,函数的形参并不占有实际的内存空间,也没有实际的值。 只有在函数被调用时才为形参分配存储单元,并将实参与形参结合。 实参类型必须与形参相符。 函数的参数传递指的就是形参与实参结合的过程。一、值调用 传值调用 直接将实参的值传递给形参。 单向传递。 形参的改变不会影响到实参。38例4-10,传值调用,将两个数的值换位。39执行主函数中的函数调用语句:swap(x,y);11xa22y
15、b在swap 子函数中:1x2y1a2b1x2y1x2y2a2b2a1btemp = a;a = b;b = temp;1temp1x2y1temp1temp返回主函数后:40 例4-11,从键盘上输入字符,要求输入字符为0-9十个数字。41二、 数组作为函数参数 数组元素可以作为调用函数时的实参,用法同单个变量,是单向传递 数组名做函数的参数函数调用时只需传递数组名。 在函数定义中,形参的类型必须与实参数组的相同,数组的大小不必指定。 函数原型中的参数也必须定义为一个数组。 例4-12:调用一个函数求数组元素之和4243 在C语言中,数组名表示的是该数组的第一个元素的地址。 传递数组名时,实
16、际上是把数组的地址传递给被调用函数。这样被调用函数中的数组就指向内存中相同的数组了。因此,在被调函数中,对形参数组元素的任何修改都将反映到调用函数的原始数组(实参数组)中。 把参数的地址传递给函数称为地址传递(pass by address)或指针传递。谨 记44例4-13, 请编写一个程序,使用一个函数来把有10个整数元素的数组按升序排列4546例4-14:调用一个函数求二维数组元素的最大值47例4-15:矩阵乘法484950三、结构体作为函数的参数分别传递各个结构成员像普通变量一样来处理;如果结构很大,该方法就变得难以控制,效率也不高。传递整个结构将整个结构的副本传递给被调函数,函数对结构
17、成员的任何修改都不会影响实参。51例,编写几个对点和矩形操作的函数。定义点的结构类型定义矩形的结构类型521. 定义函数makepoint,它带有两个整型参数,并返回一个point类型的结构532. addpoint函数:将两个点相加54注意要点:当函数的参数是一个结构类型时,主调、被调函数中相对应的实参、形参必须为相同的结构类型。当函数的返回值是一个结构时,必须将返回值赋给调用函数中的相同类型的结构变量。和其它类型的参数一样,结构类型的参数也是通过“值传递的。例4-16,以将3个学生按成绩由高到低排序为例55564.5 函数的递归调用函数可以直接或间接地调用自身,称为递归调用。直接调用:间接
18、调用:void fun1(void) fun1( ); / 调用fun1自身 void fun1(void) fun2( ); void fun2(void) fun1( ); 57 递归的条件1须有完成函数任务的语句。2有一个确定是否能防止无限的递归调用的测试。3一个递归调用语句。该语句的参数应该逐渐逼近不满足条件,以致最后断绝递归。4先测试,后递归调用。#include void count(int val) / 递归函数可以没有返回值 if (val 1) count(val -1); printf(ok:, val); / 显示:“ok:整数值 printf(n);58例4-17,有5
19、个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人岁数,他又说比第2个人大2岁。问第2个人岁数,他说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人多大?“推理age(5) = age(4) + 2age(4) = age(3) + 2age(3) = age(2) + 2age(2) = age(1) + 2age(1) = 1“总结 由此计算出各年龄59递归的过程:第一阶段:回归。第二阶段:递推。60例4-18, 计算n!以5!为例,求值过程:5!5*4*3*2*15*(4!)5!5 * 4!4 * 3!3 * 2!2 * 1
20、!1处理递归调用每个递归调用向调用者的返回值5!5 * 4!4 * 3!3 * 2!2 * 1!1返回1返回2!2返回3!6返回4!24返回5!120最后值120616263 例4-19:汉诺塔问题有三根针A ,B,C。A针上有n个盘子,盘子大小不等,大的在下,小的在上,如图示。要求把这n个盘子从A针移到C针,在移动过程中可以借助B针,每次只允许移动一个盘子,且在移动过程中在三根针上都保持大盘在下,小盘在上。ABC将n个盘子从A针移到C针可以分解为下面三个步骤:1)将A上n-1个盘子移到B针上借助C针;2)把A针上剩下的一个盘子移到C针上;3)将n-1个盘子从B针移到C针上借助A针;6465递
21、归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栈。递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写与理解。在描述树等递归定义的数据结构时,使用递归尤其方便。664.6 局部变量和全局变量一、局部变量在函数内部定义的变量,仅在该函数内有效。 不同函数中可以使用相同名字的变量,互不干扰。 局部变量在定义时没有明确的初始化值。 在函数开始运行时,局部变量在栈区中被分配空间,函数退出时,局部变量随之消失。形式参数也是局部变量。例: void main( ) void func( ) int s; int s; 67二、全局变量 在任何函数之外定义
22、的变量,也称为外部变量。 可以被本文件中其它函数所共用。它的有效范围是从定义变量的位置开始一直到根源文件结束。 存放在内存的全局数据区。 由编译器建立,默认初始化为0。 int n=5; / 全局变量 void func( ) void main( ) int s; int m = n; n = s; 68说明全局变量的作用是增加了函数之间数据联系的渠道。假设在一个函数中改变了全局变量的值,就能影响到其它函数。不成文的规定:全局变量的第一个字母用大写表示。不在特别必要时,尽量不要使用全局变量全局变量在程序的全部执行过程中都占用存储单元。降低了函数的通用性。假设外部变量与其它文件的变量同名,移植
23、时就会出现问题,降低了程序的可靠性和通用性。降低程序的清晰性。一般要求把C程序中的函数做成一个封闭体,除了可以通过“实参形参的渠道与外界发生联系外,没有其它渠道移植性好,可读性强。694.7 作用域与可见性作用域又称作用范围,它指的是标识符的有效范围 可见性指的是标识符是否可以被引用。一、作用域分类函数原型作用域 是C 程序中最小的作用域 开始于函数原型声明的左括号,结束于函数原型声明的右括号。 double area ( double width, double length);70块作用域局部作用域 块是一对大括号括起来的一段程序 块语句 在块中声明的标识符,其作用域从声明处开始,一直到块
24、结束的大括号为止。 具有块作用域的标识符称作局部标识符。void fun( int y ) / y 的作用域从此开始 int a,b; / a, b的作用域从此开始 if ( y0 ) int x; / x 的作用域从此开始 x = a + b; / x 的作用域到此结束 / a, b, y 的作用域到此结束 71void fun( int n) if( n5) int i; i=n; printf(%dn, i); else double i; i=n; printf(%fn, i); printf(%dn, i); / int i 的作用域从此开始/ int i 的作用域到此结束/ dou
25、ble i 的作用域从此开始/ double i 的作用域到此结束/ error! i 无定义72文件作用域全局作用域 在所有函数定义之外说明; 开始于声明点,结束于文件尾(全局变量。 在头文件的文件作用域中所进行的声明,假设该头文件被一个源文件嵌入,那么声明的作用域也扩展到该源文件中,直到源文件结束。int number;int value; void main( ) a = 1; / error! a 无定义 number = 0; value = number + 1; printf(“%dn, i); int a;73二、可见性 程序运行到某一点,能够引用到的标识符,就是该处可见的标识
26、符。 作用域可见性的一般规那么: 标识符要声明在前,引用在后。 在同一作用域中,不能声明同名的标识符。 在没有互相包含关系的不同的作用域中声明的同名标识符互不影响。 如果在两个或多个具有包含关系的作用域中声明了同名标识符,那么外层标识符在内层不可见。74例4-20,作用域可见性实例175例4-21,作用域可见性实例2764.8 变量的存储类型一、动态存储方式与静态存储方式程序的内存区域:静态存储方式:在程序的运行期间分配固定的存储空间动态存储方式:在程序的运行期间根据需要进行动态的分配存储空间。 程序内存空间 代码区(code area)全局数据区(data area) 堆区(heap are
27、a) 栈区(stack area)存放程序的代码全局数据和静态数据程序的动态数据程序的局部数据77动态存储区存放的数据有:函数的形式参数自动变量函数调用时的现场保护和返回地址在程序执行过程中,如果在同一个程序中两次调用同一函数,分配给此函数中局部变量的存储空间地址可能是不相同的。C语言中,变量和函数的属性除了具有数据类型外,还具有存储类型: auto自动的:采用堆栈方式分配内存空间,属于暂时性存储,其存储空间可以被假设干变量屡次覆盖使用。 static静态的:在内存中是以固定地址存放的,在整个程序运行期间都有效。 register存放器的:存放在通用存放器中。 extern外部的:在所有函数和
28、程序段中都可引用。78二、auto变量函数的形参、其中定义的局部变量等,如不专门声明为static,都是动态地分配存储空间的,数据存储在动态存储区中。 自动变量“auto关键字可以省略。假设在定义变量时不写auto,那么隐含确定为“自动存储类型。程序中大多数变量属于自动变量。auto int a=3, b; int a=3, b; 等价79三、用static声明局部变量静态局部变量,在静态存储区内分配存储单元。变量在程序的整个运行期间都不释放。 static int i; static char bufBUFSIZE; 静态存储变量,假设无显式初始化,那么在程序编译时自动初始化为 0。即只初始
29、化一次,以后每次调用函数时不再重新赋初值,而只是保存上次函数调用结束时的值。虽然静态局部变量在函数调用结束后仍然存在,但其它函数是不能引用它的。80例4-22,静态存储变量举例81第n次调用调用时初值调用结束时的值bcbca+b+c第1次第2次第3次000345111456891082四、register变量局部变量的值直接存放在CPU中的存放器中。只有局部自动变量和形式参数可以作为存放器变量,其它如全局变量、局部静态变量不行。 register int x; register char c; f(register unsigned m, register long n) register int I; register static int a, b; 错!一个计算机系统中的存放器数目是有限的,不能定义任意多个存放器变量。83五、外部变量即全局变量,是在函数的外部定义的,它的作用域为从变量的定义处开始,到本程序文件的末尾。在此作用域中,全局变量
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年小学语文老师招聘备考题库及参考答案详解1套
- 2025年大涌医院第四期公开招聘工作人员备考题库及答案详解1套
- 2025年黄埔海关国际旅行卫生保健中心公开招聘非占编聘用人员的备考题库有答案详解
- 2025年中国大唐集团核电有限公司系统各岗位公开招聘5人备考题库及1套参考答案详解
- 2025年广州市花都区新雅街镜湖学校招聘临聘教师备考题库及答案详解一套
- 2025年清华大学附属小学教育集团邀您来备考题库附答案详解
- 中国铝业集团有限公司2026年度高校毕业生招聘1289人备考题库及一套答案详解
- 理想课件教学
- 班级蛋糕DIY课件
- 班级科普宣传课件
- 2025年西昌市邛海泸山风景名胜区管理局招聘5名执法协勤人员备考题库有答案详解
- 2025年杭州市公安局上城区分局警务辅助人员招聘60人备考题库及完整答案详解一套
- 2025中央社会工作部所属事业单位招聘11人笔试试题附答案解析
- 2025国开期末考试《中国现代文学专题》机考试题含答案
- 居民自管小组建设方案
- 2025年煤矿安全生产治本攻坚三年行动工作总结
- 美团代运营服务合同协议模板2025
- 2025江苏南京市市场监督管理局所属事业单位招聘高层次人才5人(公共基础知识)测试题带答案解析
- 2025年二级建造师继续教育考试题库及答案
- 泵站、水闸混凝土施工实施细则
- (一模)2025年嘉兴市2026届高三教学测试思想政治试卷(含答案)
评论
0/150
提交评论