第3章函数和编译预处理ppt课件.ppt
《第3章函数和编译预处理ppt课件.ppt》由会员分享,可在线阅读,更多相关《第3章函数和编译预处理ppt课件.ppt(111页珍藏版)》请在三一办公上搜索。
1、第3章 函数和编译预处理,3.1 函数概述 3.2函数的定义和调用 3.3 函数的参数传递 3.4 函数的嵌套调用和递归调用 3.5 内置函数 3.6 变量和函数的属性 3.7 编译预处理,3.1 概述,一个较大的程序不可能完全由一个人从头至尾地完成,更不可能把所有的内容都放在一个主函数中。为了便于规划、组织、编程和调试,一般的做法是把一个大的程序划分为若干个程序模块(即程序文件),每一个模块实现一部分功能。不同的程序模块可以由不同的人来完成。在程序进行编译时,以程序模块为编译单位,即分别对每一个编译单位进行编译。如果发现错误,可以在本程序模块范围内查错并改正。在分别通过编译后,才进行连接,把
2、各模块的目标文件以及系统文件连接在一起形成可执行文件。,在一个程序文件中可以包含若干个函数。无论把一个程序划分为多少个程序模块,只能有一个main函数。程序总是从main函数开始执行的。在程序运行过程中,由主函数调用其他函数,其他函数也可以互相调用。在C语言中没有类和对象,在程序模块中直接定义函数。可以认为,一个C程序是由若干个函数组成的,C语言被认为是面向函数的语言。C+面向过程的程序设计沿用了C语言使用函数的方法。在C+面向对象的程序设计中,主函数以外的函数大多是被封装在类中的。主函数或其他函数可以通过类对象调用类中的函数。无论是C还是C+,程序中的各项操作基本上都是由函数来实现的,程序编
3、写者要根据需要编写一个个函数,每个函数用来实现某一功能。因此,读者必须掌握函数的概念以及学会设计和使用函数。,“函数”这个名词是从英文function翻译过来的,其实function的原意是“功能”。顾名思义,一个函数就是一个功能。在实际应用的程序中,主函数写得很简单,它的作用就是调用各个函数,程序各部分的功能全部都是由各函数实现的。主函数相当于总调度,调动各函数依次实现各项功能。开发商和软件开发人员将一些常用的功能模块编写成函数,放在函数库中供公共选用。程序开发人员要善于利用库函数,以减少重复编写程序段的工作量。,图3.是一个程序中函数调用的示意图。 图3.,例31 在主函数中调用其他函数。
4、#include /*ex3_1.cpp*int calc_sum(int n) /定义calc_sum()函数 int k,s; s=0; for (k=1;kn; if (n1) coutthe sum is: calc_sum(n)endl; /调用calc_sum()函数 print_word(); /调用rint_word()函数,若用户从键盘输入的数是4, 运行情况如下:the sum is:10Hello,C+! 从用户使用的角度看,函数有两种:(1) 系统函数,即库函数。这是由编译系统提供的,用户不必自己定义这些函数,可以直接使用它们。(2) 用户自己定义的函数。用以解决用户的
5、专门需要。从函数的形式看,函数分两类:(1) 无参函数。调用函数时不必给出参数。(2) 有参函数。在调用函数时,要给出参数。在主调函数和被调用函数之间有数据传递。,3.2 函数的定义和调用,3.2.1 定义无参函数的一般形式定义函数的一般形式如下:类型标识符 函数名(形式参数列表) 声明语句执行语句,【例3.1】的print_word函数是无参函数,它的定义中首句也可以写成:void print_word( ) /定义print_word()函数的首部; 或:void print_word(void) /定义print_word()函数的首部【例3.1】的calc_sum函数是有参函数,不过它
6、只有一个参数,参数的类型是整数类型。 int calc_sum(int n) /定义calc_sum()函数 C+要求在定义函数时必须指定函数的类型。下面的函数f则有两个参数,第一个是整数类型,第二个是单精度实数类型,而函数的返回值是双精度实数类型:double f(int x, float y) /定义f( )函数的首部,(1)对库函数的声明其实,对库函数的声明语句已经写在有关包含文件中了,因此只要在程序文件头用include语句将这些包含文件包含到本程序中来,就完成了对库函数的声明。 (2)对自定义函数的声明必须在调用某自定义函数的语句之前写上如下声明语句:函数类型关键字 函数名(参数1类
7、型,参数1名称,参数2类型,参数2名称);,3.2.2 函数的声明,(2)对自定义函数的声明 (续)也可以在声明语句中略去参数的名称,或写一个任意名称,这叫做函数原型(function prototype),即声明函数原型。函数原型有下列两种表示形式:函数类型关键字 函数名(参数1类型,参数2类型);函数类型关键字 函数名(参数1类型,标识符1,参数2类型,标识符2);C+中的函数原型说明了函数的类型、函数名、函数各形式参数类型。使用函数原型是C和C+的一个重要特点。,3.2.2 函数的声明,【例3.2】 对被调函数做声明的示例。#include /*ex3_2.cpp*void main()
8、 float add(float x,float y); /对add函数作声明 float subtract(float,float); /对subtract函数作声明,用函数原型 double multiply(float p,float q); /对multiply函数作声明,参数名任意 float a,b,c1,c2; double c3; cout ab; c1=add(a,b); c2=subtract(a,b); c3=multiply(a,b); cout c1,c2,c3endl; ,float add(float x,float y) /定义add函数 float z; z=
9、x+y; return (z);float subtract(float x,float y) /定义subtract函数 float z; z=x-y; return (z);double multiply(float x,float y) /定义multiply函数 double z; z=x*y; return (z);运行情况如下:please input a,b:4.25 2.00 6.25,2.25,8.5,说明:(1)对函数的定义和函数声明是两回事,不要混淆。 (2)之所以函数原型中可以省略形式参数的名称,是因为形式参数的名称是无关紧要的,且在调用前形参并不存在。 (3)函数声明
10、语句的位置。函数声明语句可以放在主调函数中,也可放在函数外面,只要出现在调用语句之前即声明有效。,3.2.3 函数的返回值,(1) 函数的返回值是通过函数中的return语句获得的。return语句将被调用函数中的一个确定值带回主调函数中去。return语句后面的括号可以要,也可以不要。return后面的值可以是一个表达式。(2) 函数值的类型。既然函数有返回值,这个值当然应属于某一个确定的类型,应当在定义函数时指定函数值的类型。(3) 如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准,即函数类型决定返回值的类型。对数值型数据,可以自动进行类型转换。,第(1)种形式是基本
11、的形式。为了便于阅读程序,也允许在函数原型中加上参数名,就成了第(2)种形式。但编译系统并不检查参数名。因此参数名是什么都无所谓。上面程序中的声明也可以写成float add(float a,float b); /参数名不用x、y,而用a、b 效果完全相同。应当保证函数原型与函数首部写法上的一致,即函数类型、函数名、参数个数、参数类型和参数顺序必须相同。在函数调用时函数名、实参类型和实参个数应与函数原型一致。,说明:,(1) 前面已说明,如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已经事先知道了已定义的函数类型,会根据函数首部提供的信息对函数的调用作正确性检查。有经验
12、的程序编制人员一般都把main函数写在最前面,这样对整个程序的结构和作用一目了然,统览全局,然后再具体了解各函数的细节。此外,用函数原型来声明函数,还能减少编写程序时可能出现的错误。由于函数声明的位置与函数调用语句的位置比较近,因此在写程序时便于就近参照函数原型来书写函数调用,不易出错。所以应养成对所有用到的函数作声明的习惯。这是保证程序正确性和可读性的重要环节。,(2) 函数声明的位置可以在调用函数所在的函数中,也可以在函数之外。如果函数声明放在函数的外部,在所有函数定义之前,则在各个主调函数中不必对所调用的函数再作声明。例如: char letter(char,char); /本行和以下两
13、行函数声明在所有函数之前且在函数外部float f(float,float); /因而作用域是整个文件 int i(float, float); int main( ) /在main函数中不必对它所调用的函数作声明char letter(char c1,char c2) /定义letter函数float f(float x,float y) /定义f函数 int i(float j,float k) /定义i函数如果一个函数被多个函数所调用,用这种方法比较好,不必在每个主调函数中重复声明。,函数调用的一般形式:函数名(实际参数列表) 如果是调用无参函数,则“实参表列”可以没有,但括号不能省略。
14、如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应匹配(相同或赋值兼容)。实参与形参按顺序对应,一对一地传递数据。但应说明,如果实参表列包括多个实参,对实参求值的顺序并不是确定的。,3.2.4函数的调用,按函数在语句中的作用来分,可以有以下3种函数调用方式:. 函数语句把函数调用单独作为一个语句,并不要求函数带回一个值,只是要求函数完成一定的操作。如【例3.1】中的print_word()函数调用语句。 2. 函数表达式函数出现在一个表达式中,这时要求函数带回一个确定的值以参加表达式的运算。如c=2*max(a,b);3. 函数参数函数调用作为一个函数的实参。如m=
15、max(a,max(b,c); /max(b,c)是函数调用,其值作为外层max函数调用的一个实参,函数调用的方式,3.3 函数的参数传递 形参:在定义函数时函数名后面括号中的变量名称为形式参数(formal parameter),简称形参。形参是无内存单元(因而不存在)的任何合法标识符。实参:在调用一个函数时,调用语句的函数名后面括号中的参数称为实际参数(actual parameter),简称实参。实参是实际存在(因而有特定值)的常量、变量或表达式。,【例3.3】 形参和实参及其数据传递。#include /*ex3_3.cpp* using namespace std;float vol
16、ume(float r,float h) /定义有参函数volume求圆柱体体积,r和h是形参 float v;v=3.14*r*r*h; return (v);void main()float a,b,c;cout a b;c=volume(a,b); /调用函数volume,a和b是实参。函数值赋给变量ccout The volume is cendl; ,运行情况如下:please input two float nukbers:2.0 5.0 The volume is 62.8,有关形参与实参的说明:(1) 在定义函数时指定的形参,在未出现函数调用时,它们并不占内存中的存储单元,因此
17、称它们是形式参数或虚拟参数,表示它们并不是实际存在的数据,只有在发生函数调用时,函数max中的形参才被分配内存单元,以便接收从实参传来的数据。在调用结束后,形参所占的内存单元也被释放。(2) 实参可以是常量、变量或表达式,如max(3, a+b);但要求a和b有确定的值。以便在调用函数时将实参的值赋给形参。(3)在定义函数时,必须在函数首部指定形参的类型,至于形参使用何名字可随意。,(4)实参与形参的类型应相同或赋值兼容。两种参数类型完全一致无疑是完全合法、正确的。如果实参为整型而形参为实型,或者相反,则按不同类型数值的赋值规则进行转换,原则上不出现语法错误,但结果可能带来某些非期望的误差。例
18、如实参a的值是3.5,而被调函数的形参x为整型,则调用该函数时会将3.5转化为整数3,然后送到形参x,故x得到的是3,由3计算出的函数值与人们期望由3.5计算出的函数值一般是有差别的。但字符型与整型可以互相通用。,3.3.2 参数的值传递 值传递参数的实现是系统将实参拷贝一个副本给形参,拷贝后两者就断开关系。在被调函数中,形参可以被改变,但这只影响副本中的形参值,而不影响调用函数的实参值。所以这类函数有对原始数据保护的作用。换一句话说,这种参数传递机制是单向影响,即只能由实参将值传给形参(实参影响形参);而形参在函数中的值如果发生修改,不会反过来影响与之对应的实参。,【例3.4】 参数值传递的
19、演示。#include /*ex3_4.cpp* using namespace std;int max(int x,int y) /定义有参函数max求两数最大值,x和y是形参 float m;couty?x:y;x=2*x;y=y+1;coutThe new x,y:x,yendl;return (m);,void main()float a,b,c;cout a b;c=max(a,b); /调用函数max,a和b是实参,函数值赋给变量c。cout The maxinum is: cendl;cout After calling fuction,a,b:a,bendl;运行情况如下:pl
20、ease input two integer numbers:3 8 The initial x,y:3,8The new x,y:6 9The maxinum is:8After calling fuction,a,b:3,8,3.3.3 参数的地址传递除了3.3.2小节介绍的值传递参数方式外,函数调用还有一种特殊的值传递形式,即传递的值不是一般的数值,而是一些内存单元地址编号(即地址),这时,一般称之为参数的地址传递。在这种参数传递形式中,无论在函数的定义中出现的形参还是在调用语句中出现的实参,都是代表一些内存单元地址编号(即地址数值),而不是一般的数值。C+中的参数地址传递情况一般有如下
21、几种:实参可以是一个有确定值的普通变量的地址,或者是一个已经初始化的指针变量;或者是一个初始化的数组名;或者是一个具体的函数名。而形参可以是一个任意普通变量的地址,或是一个任意指针变量,或是一个任意的数组名,或是一个指向函数的指针变量(对应于实参是具体函数名)。,实际上,这种参数传递机制就是在函数调用时把一个内存单元地址传递给形参,使形参也具有实参的内存单元地址(即两者对应同一个内存单元),称作形参和实参地址结合,两者合二为一。这样一来,任何时候形参的值等于实参的值;而实参的值也等于形参的值。因此,形参在函数中发生变化后,也会引起实参跟着变化(因为它们是捆绑在一起的,一体化的)。这就意味着按地
22、址传递的方式,在调用刚开始时实参的值影响了形参;而在被调函数执行过程中形参值若发生了变化,它也会影响实参的值变化。即机制是双向影响,这与普通值传递方式的单向影响机制形成对比。,3.3.4 带默认值的参数C+语言中,允许在函数声明或定义时给一个或多个参数指定默认值。通常调用函数时,要为函数的每个形式参数给定相应的值。例如下面的delay( )函数作用是作时间延迟,在没有使用默认值参数的情况下,是按如下普通方式声明和定义的: 【例3.5】 延迟函数的使用。#include /*ex3_5.cpp* void delay(int loop); /函数声明void main( ) coutbegine
23、ndl; delay(1000); /函数调用 coutendendl;void delay(int loop) /函数定义 if (loop=0) return; for(int i=0;iloop;i+) coutiendl; /输出i的值为了清楚看到程序执行的情况,如果每次调用延迟时间基本一样,可以使用C+中默认值参数函数形式来解决问题。解决的方法就是在函数的声明(或定义)时给定默认值即可。具体做法只要把delay()函数的声明改为下列形式: void delay(int loop=1000); /指定参数默认值为1000以后如果需要延迟相同时间1000,都可以不必指定实参的值而直接调用
24、函数:delay(); /不给定实参,形参将得到默认值1000 delay(500); /给定实参,形参将得到所给的值500,如果有多个形参,可以使每个形参有一个默认值;也可以只对一部分形参指定默认值。如前面的求圆柱体体积的函数volume,可以这样声明:float volume(float r,float h=8.5); /只对形参h指定默认值8.5这时函数调用可采用以下形式:volume(6.0); /相当于volume(6.0,8.5)volume(6.0,7.2); /r的值为6.0,h的值为7.2 C+中实参和形参的结合是从左至右进行的,第1个实参必然与第1个形参结合,第2个实参必然
25、与第2个形参结合,。因此,指定默认值的参数必须放在参数列表中的最右边。,3.4 函数的嵌套调用和递归调用,C+不允许对函数作嵌套定义,也就是说在一个函数中不能完整地包含另一个函数。在一个程序中每一个函数的定义都是互相平行和独立的。虽然C+不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。见图3.2示意。 图3.2,在程序中实现函数嵌套调用时,需要注意的是: 在调用函数之前,需要对每一个被调用的函数作声明(除非定义在前,调用在后)。【例3.6】编程求组合,其中求组合的功能要求用函数完成。分析:根据组合的计算公式,知组合函数有两个形参:m和n,可以用自定义函
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 函数 编译 预处理 ppt 课件
链接地址:https://www.31ppt.com/p-1401465.html