《第4章 函数和预处理精选PPT.ppt》由会员分享,可在线阅读,更多相关《第4章 函数和预处理精选PPT.ppt(83页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第第4章章 函数和预处理函数和预处理第1页,本讲稿共83页目录目录4.1 函数的概述4.2 函数的定义和调用4.3 函数的参数传递4.4 内联函数4.5 递归函数4.6 函数重载4.7 作用域4.8 文件与预编译命令第2页,本讲稿共83页调用函数1调用函数2.函数1 函数2图4.1 函数调用图4.1函数概述函数概述 当一个程序在调用某个函数时,当一个程序在调用某个函数时,当一个程序在调用某个函数时,当一个程序在调用某个函数时,c+c+自动转到被调用的函数中执行,自动转到被调用的函数中执行,自动转到被调用的函数中执行,自动转到被调用的函数中执行,执行完后再回到原先程序执行的位置,继续执行下一条语
2、句。执行完后再回到原先程序执行的位置,继续执行下一条语句。执行完后再回到原先程序执行的位置,继续执行下一条语句。执行完后再回到原先程序执行的位置,继续执行下一条语句。目录第3页,本讲稿共83页4.2 函数定义和调用函数定义和调用函数定义函数定义在使用函数时,要先对函数进行定义,确定它要实现的功能。函数调用函数调用 函数的使用就是调用函数的过程。第4页,本讲稿共83页4.2.1 函数定义函数定义形式如下:()函数体;函数名是有效c+标识符,其后必须跟(),以区别于变量名及其他用户定义的标识名。即函数的返回值类型,若不需要函数有返回值,则定义函数的类型为void即可。各参数以逗号隔开,每个参数由参
3、数类型和参数名表示。参数个数可以为,但圆括号不能省略。由 括起来的若干条语句组成,实现这个函数执行的功能。注:注:C+不允许在一个函数体不允许在一个函数体中再定义另一个函数中再定义另一个函数第5页,本讲稿共83页函数返回值函数返回值int max(int x,int y)return (xy?x:y);/返回整型 函数返回值语法 return;将return后面的值作为函数返回值,类型同函数定义处的类型相匹配第6页,本讲稿共83页注意:注意:若函数类型为void,函数体中就不需要return或者return的后面什么也没有 例:无返回值函数void max(int x,int y)couty?
4、x:y);/无return 一旦执行return语句,函数体return后的语句不再执行例:求负数绝对值int abb(int x)if(x0)return x;cout“this is负数”;return x;第7页,本讲稿共83页4.2.2 函数调用函数调用调用函数的一般形式如下:()实际参数与形参相对应,它是实际调用函数时所给定的常量,变量或表达式,且必须有确定的值。一般地,仅当函数被调用时,系统才会给形参分配内存单元,而调用结束后,形参所占用的内存单元又被释放。第8页,本讲稿共83页例例:输输入入两两个个实实数数,输输出出其其中中较较大大的的数数。其其中中求求两两个个实实数数中中的的较
5、较大大数数用用函函数完成。数完成。#includefloat max(float x,float y)return(x=y?x:y);void main()float x,y;cout输入两个实数:输入两个实数:xy;coutx和和y中较大数为中较大数为max(x,y)endl;main()函数调用max(2.5,4.7)函数max(2.5,4.7)return 4.7 主程序后续语句第9页,本讲稿共83页实参与形参的个数相等,类型一致,且顺序一致实参与形参的个数相等,类型一致,且顺序一致函数调用方式:函数调用方式:1.作为一个语句不使用返回值作为一个语句不使用返回值 max(3,4);/只是
6、要求函数完成一定的操作2.作为表达式的一部分作为表达式的一部分int c=5*max(2,10);/将返回值参与运算,结果c=503.作为函数的实参作为函数的实参int c=2;c=max(max(c,4),10);/等价于c=max(max(2,4),10),最后结果为c=10目录第10页,本讲稿共83页4.2.3函数的说明函数的说明背景:函数必须先定义后调用用途:若函数定义在后而调用在前,需在调用前进行“函数的说明”。函数说明的格式:();形参的变量名可以省略。但要注意,函数说明的内容应与形参的变量名可以省略。但要注意,函数说明的内容应与函数的定义相同。必须有分号!函数的定义相同。必须有分
7、号!例:int max(int x,int y);或int max(int,int);第11页,本讲稿共83页例:输入两个实数,输出其中较大的数。其中求两个实数中的较大数用函数完成。#includefloat max(float,float);/说明以分号结尾void main()float x,y;cout输入两个实数:xy;coutx和y中较大数为max(x,y)=y?x:y);目录第12页,本讲稿共83页4.3函数的参数传递函数的参数传递参数类型检查C+参数的传递方式:按值传递地址或引用传递第13页,本讲稿共83页1.按值传递按值传递参数传递的缺省初始化方式是把实参的值拷贝到形参的存储区
8、。函数本身不对实参进行操作,只是处理的实参的本地拷贝。也就是说,即使形参的值在函数中发生了变化,实参的值不会受到影响。第14页,本讲稿共83页#includevoid swap(int x,int y)int temp=x;x=y;y=temp;void main()int a=2,b=10;swap(a,b);couta=a,b=b;cout引用在声明时必须初始化引用在声明时必须初始化引用的声明形式:&引用名=变量名int a,b=10;int&i=a;i=5;i=b;注意:引用仅在说明的时候带&,之后就象普通变量一样使用,不再带&对引用的操作实际是对其引用的变量的操作引用一旦声明,就不能再
9、成为其他变量的别名引用不是变量,它不占存储空间,它与被引用的变量有同存储地址引用不是指针,不能动态分配内存a,i第16页,本讲稿共83页一个函数能使用引用传递方式是在函数定义时将形一个函数能使用引用传递方式是在函数定义时将形参前面加上引用运算符参前面加上引用运算符“&”。例如:例如:void swap(int&x,int&y)第17页,本讲稿共83页#includevoid swap(int&x,int&y)int temp=x;x=y;y=temp;void main()int a=2,b=10;swap(a,b);cout a=a,b=b endl;函数函数swap中的中的int&x和和i
10、nt&y就是形参的引用就是形参的引用说明。说明。在执行在执行swap(a,b)时,时,x、y相当于相当于a、b的别名,的别名,对引用的操作实际是对其引用的变量的操作,因此对形参形参x,y任何操作都会改变相应的实参任何操作都会改变相应的实参a,b的数据。的数据。所以所以swap调用后调用后a和和b的值交换的值交换本程序执行结果如下:本程序执行结果如下:a=10 b=2第18页,本讲稿共83页引用适用于:需要改变参数值的函数接受大类型对象做参数的函数当一个函数返回个引用时,它可成为左值。结构体或对象第19页,本讲稿共83页3.地址传递地址传递函数定义时将形参说明成指针。int n=1;/n的内容为
11、1int*p=&n;/p为整型指针变量,其内容是n的地址*p表示p指向的整型变量的值;调用函数时需要指定地址值形式的实参。通过改变形参(指针)指向的变量来影响实参。存放变量的地址第20页,本讲稿共83页#includevoid swap(int*x,int*y)int temp=*x;*x=*y;*y=temp;void main()int a=2,b=10;swap(&a,&b);cout a=a,b=b endl;结果:结果:a=10,b=22(*x)10(*y)0 x000E0 x001E0 x000E a a0 x001E b bx xy y地址地址内容内容变量名变量名10102 2第
12、21页,本讲稿共83页参数传递的总结参数传递的总结值传递值传递引用和地址传递引用和地址传递改变实参副本(拷走)改变实参本身(操作)第22页,本讲稿共83页4函数的默认参数值函数的默认参数值C+允许在函数的说明或定义时给一个或多个参数指定默认值。这样在调用时可以不给出参数,而按指定的默认值进行工作。定义void initialize(int printNO,void initialize(int printNO,int state=0int state=0)调用initialize(1);/打印机状态缺省为0initialize(1,0);/传递打印机状态参数0initialize(1,1);第
13、23页,本讲稿共83页1.函数可以将其全部或部分参数定义为带默认值,但带默认值的参数只能放在参数表的最后 例:以下函数定义不正确:void f(int par1=1,int par2,int par3=3)void f(int par1=1,int par2=2,int par3)因为系统按从前往后的顺序进行参数匹配若中间参数有默认值,无法判断哪些参数使用默认值f(5);f(3,6);f(2,3,4);注意:第24页,本讲稿共83页注意2函数既有定义又有说明时,一般函数既有定义又有说明时,一般说明中说明中给出给出默认形参值。默认形参值。void initialize(int printNO,i
14、nt state=0);同一作用域同一作用域中,函数某一参数的中,函数某一参数的默认值默认值只能只能说明一次说明一次void initialize(int printNO,int state=0);void initialize(int printNO,int state=1);/错!在同一全局作用域内重复说明 /state默认值void main()void initialize(int printNO,int state=1);/可以!在不同作用域内void initialize(int printNO,int state)第25页,本讲稿共83页程序还可以通过重新定义函数原型使本来不带默
15、认值的参数带上默认值,这是使通用函数特定化例:以下声明合法:(同一作用域)void initialize(int printNO,int state=0);void initialize(int printNO=1,int state);/对!重新定义printNO的默认值注意3第26页,本讲稿共83页#include void initialize(int printNO,int state=0);void initialize(int printNO=1,int state);/重新定义重新定义 printNO的默认值的默认值void main()initialize();initiali
16、ze(0);initialize(1,1);void initialize(int printNO,int state)cout “printNO=”printNO“,”;cout“state”state endl;本程序的运行结果如下本程序的运行结果如下 ptintNO=1,state=0 ptintNO=0,state=0 ptintNO=1,state=1第27页,本讲稿共83页函数调用机制函数调用机制main().调func().保存:返回地址当前现场恢复:主调函数现场返回地址void func().目录第28页,本讲稿共83页4.4 作用域作用域作用域(讨论标识符的有效范围)作用域(
17、讨论标识符的有效范围)变量的作用域是指程序中变量有效的区域。分为三类:文件域局部域类域。第29页,本讲稿共83页#includeint global;void f(int par)int flocal;if(global)int blocal;blocal=par;blocal=1;flocal=1;global=1;void main()int local=2;f(local);global/全局变量localparflocalblocal全局变量全局域定义的变量(定义在所有函数之外的变量)局部变量函数、块中定义的变量局部变量global作用域flocal作用域blocal作用域local作
18、用域第30页,本讲稿共83页注意注意同一作用域,不能声明同名的标志符不同作用域的标识符可以重名,但是具有包含关系的作用域中,局部变量会隐藏局部变量会隐藏同名全全局变量局变量在没有互相包含关系的作用域中,声明的标志符互不影响具有包含关系的作用域中声明了同名标志符,则外层标志符在内层不可见(局部变量可隐藏同名全局变量)第31页,本讲稿共83页#include int n=1;/n为全局变量为全局变量 void Fun()int n=10;/n为函数内局部变量为函数内局部变量 cout“Fun:n=”n 0)int n=5;/n为块内局部变量为块内局部变量,掩盖了全局的掩盖了全局的n cout“Bl
19、ock:n=”n endl;cout“Main:n=”n endl;/n为全局变量为全局变量 Fun:n=10Block:n=5Main:n=2第32页,本讲稿共83页4.4.1 域运算符域运算符:背景:背景:局部变量可隐藏全局变量,在有同名全局和局部变量的情形时如何访问全局变量呢?域运算符域运算符“:”提供对全局变量的访问。提供对全局变量的访问。以以域运算符为前缀的变量表示全局变量。域运算符为前缀的变量表示全局变量。例如,下面程序中有两个变量同名,一个为全局变量,一个为局部变例如,下面程序中有两个变量同名,一个为全局变量,一个为局部变量,可以通过域运算符访问全局变量量,可以通过域运算符访问全
20、局变量:int var=10;/全局变量全局变量func()int var;/局部变量局部变量 var=:var;/将全局变量的值赋给局部变量将全局变量的值赋给局部变量第33页,本讲稿共83页 例:银行还贷计算例:银行还贷计算#include float lv=0.062;/央行房贷利率上限央行房贷利率上限 float count(float loan,float ratio)float lv=:lv*ratio;/各个银行重新计算自己的利率各个银行重新计算自己的利率 return loan*(1+lv);int main()float loan;float gh_ratio;/0.9floa
21、t zh_ratio;/0.8coutloan;/10000coutgh_ratio;coutzh_ratio;cout工行本月还贷:工行本月还贷:count(loan,gh_ratio)endl;cout中行本月还贷:中行本月还贷:count(loan,zh_ratio);贷款总额贷款总额10000工行执行:工行执行:0.8中行执行:中行执行:0.9工行本月还贷:工行本月还贷:10496中行本月还贷:中行本月还贷:10558第34页,本讲稿共83页4.4.2 变量的存储类型变量的存储类型从变量的存储角度看,有四种存储类型静态变量外部变量自动变量寄存器变量。说明格式:存储类型决定了变量的生存期
22、存储类型决定了变量的生存期!第35页,本讲稿共83页1 静态变量静态变量(static)-引子引子作用域作用域VS。生存期生存期作用域:该变量可以被引用的范围生存期:从变量定义且被分配内存开始,直到相应内存被释放为止变量的生存期变量的生存期(存储空间)(存储空间)可以是永久的(即在程序运行期间该变量一直存在)也可以是暂时的(即变量在程序运行到达其作用域时才会产生,而作用域结束时,变量也随之消亡)作用域与生存期作用域与生存期一般一般是对应的是对应的全局变量是永久的:在程序运行的过程中一直存在;局部变量大多是临时的:只在说明它们的作用域内发挥作用,一旦程序控制离开了这一作用域,这些局部变量所占空间
23、就会释放。二者交叉二者交叉static第36页,本讲稿共83页静态局部变量既有在局部作用域的特性(即变量只能在变量作用的范围内被访问),又可以永久存在。声明方式:在变量定义前加上static标记。static int local;第37页,本讲稿共83页 例:例:/银行进门排队发号银行进门排队发号#include int welcome()static int counter=0;counter+;coutwelcome!You are counterch;/Q或或q为结束为结束while(ch!=Q&ch!=q)welcome();cinch;/Q或或q为结束为结束 ewelcome!You
24、 are 1vwelcome!You are 2q第38页,本讲稿共83页静态局部变量静态局部变量VS.局部变量局部变量分配空间系统给静态的局部变量分配固定的存储空间(全局存储空间)而不是每次执行到该局部域时才分配空间,所以它能一直保持值。局部变量:每次进入其作用域时才分配空间初始化静态局部变量只在第一次执行时初始化一次,局部变量每次执行到该局部域都需要初始化第39页,本讲稿共83页静态全局变量静态全局变量 被定义为静态的全局变量只在定义它的文件中可见,其他文件中不能使用。优势信息隐藏在不同的文件中使用意义不同而同名的变量名。非静态全局变量可以做外部变量第40页,本讲稿共83页 给出以下程序的
25、执行结果:给出以下程序的执行结果:#include int global1=0;/全局变量可以在程序中显式初始化全局变量可以在程序中显式初始化 int global2;/全局变量也可以由系统隐式初始化为全局变量也可以由系统隐式初始化为0 static int global3=1;/静态全局变量静态全局变量void main()int i;/局部临时变量局部临时变量for(i=0;i3;i+)int temp=l;/局部临时变量,每次循环都会重新初始化局部临时变量,每次循环都会重新初始化 static int perm=l;/静态局部变量,只在第一次初始化静态局部变量,只在第一次初始化 cout
26、 “temp=”temp “,perm=”perm endl;+temp;+perm;temp=1,perm=1temp=1,perm=2temp=1,perm=3temp和perm释放空间吗第41页,本讲稿共83页注意注意静态局部变量静态局部变量VS.局部变量局部变量静态全局变量静态全局变量VS.全局变量全局变量暂时存在永久存在本文件使用可做外部变量第42页,本讲稿共83页2.外部变量外部变量(extern)定义文件1中的非静态全局变量需要在文件2中引用,对于文件2来说是外部变量。外部变量:extern int var;外部函数:定义在一个文件中的函数也可以由另一个文件引用:extern v
27、oid func(int,int);引用前要声明可以省略第43页,本讲稿共83页extern声明注意声明注意变量没有extern,报错!函数缺省被认为是外函数缺省被认为是外部的部的/file1.cpp#include int gInt;/全局变量隐式初始化为全局变量隐式初始化为0void setgInt(int i)gInt=i;coutin file1 SetgInt:gIntendl;/file2.cpp/int gInt;错!编译报变量重名错误错!编译报变量重名错误extern int gInt;void setgInt(int);/省略省略extern#include void mai
28、n()coutin file2:gInt=gIntendl;setgInt(10);gInt+=4;coutin file2 gInt=gIntendl;in file2:gInt=0in file1 SetgInt:10in file2 gInt=14第44页,本讲稿共83页3.自动变量自动变量(auto)声明格式 auto int n;一般来说,用自动存储类型说明的变量都限制在某个程序范围内使用,即为局部变量。当程序执行到超出该变量的作用域时,就释放他所占用的内存空间,其值也随之消失了。注:若自动存储类型的变量是在函数内或语句块中说明的,则可省略关键字auto。其实,我们在程序中见到的未说
29、明存储类型的变量都是auto变量。第45页,本讲稿共83页4.寄存器变量寄存器变量(register)声明格式 register ;例:register int n;适于频繁使用的变量一般的变量是放在内存里面的若将变量放入寄存器内,会加快了程序的运行速度。第46页,本讲稿共83页regiser注意只有局部自动变量和形参可以作为register变量,全局和静态的不可!当今的优化编译系统能识别频繁使用的变量,自动将其放在regiser中,无须人为指定第47页,本讲稿共83页自动变量静态变量 局部 全局寄存器变量外部变量(全局变量)程序结束前一直保存;多个函数共享;初始化;否 否不定有 有否 可以
30、0 0 无 否不定 有 可以 0表4.14种存储类型的变量的特征第48页,本讲稿共83页变量的生命期变量的生命期变量的生命期小结变量的生命期小结种类种类静态生命期局部生命期动态生命期静态生命期静态生命期自定义始,至程序结束止才释放存储空间如:全局变量静态全局变量静态局部变量。未被用户初始化的情况下,系统会自动将其初始化为0。永久暂时第49页,本讲稿共83页局部生命期局部生命期在在函数内部或块中函数内部或块中定义的标识符具有局部生命期。定义的标识符具有局部生命期。如果未被初始化,如果未被初始化,其内容是随机其内容是随机的。的。注:局部生命期的标识符注:局部生命期的标识符 具有局部作用域;具有局部
31、作用域;如:如:静态局部变量静态局部变量第50页,本讲稿共83页动态生命期动态生命期(后续内容介绍后续内容介绍)具有动态生命期的标识符由特定的函数调用或运算来创建和释放,如调用malloc()或用new运算符为变量分配存储空间时,变量的生命期开始,而调用free()或用delete运算符释放空间或程序结束时,变量生命期结束。具有动态生命期的变量存放在堆区。具有动态期的标识符如果未被初始化,其内容是随机的。(关于new运算和delete运算将在后面章节介绍)目录第51页,本讲稿共83页4.5 内联函数内联函数inline内联是内联扩展(inline expansion)的简称。为什么引入内联函数
32、?在编译时,C十十编译器在遇到调用内联函数的地方会用函数体中的代码来替换函数的调用好处:节省函数调用带来的参数传递、运行栈的入栈与出栈等开销,从而提高运行速度代价:增加了代码长度第52页,本讲稿共83页定义:在一般函数定义前加inline关键字。inline int abs(int x)if(x0)return x;else return x;void main()int m,m1=2,n,n1=-10;m=abs(m1);n=abs(n1);编译程序会自动用函数体编译程序会自动用函数体代替函数调用代替函数调用void main()int m,mt=2,n,n1=-10;/m=abs(m1);
33、转换代码 if(m10)m=-m1;else m=m1;/n=abs(n1);转换代码 If(nl0)n=-n1;else n=nl;第53页,本讲稿共83页注意:注意:内联函数的使用限制:内联函数一般是比较小的、经常被调用的函数。内联函数不能是递归的。内联函数中不能定义任何静态变量(本章后面介绍)。内联函数一般不能使用循环、switch和goto语句;内联函数中不能说明数组。目录第54页,本讲稿共83页4.6 递归函数递归函数嵌套嵌套递归递归第55页,本讲稿共83页嵌套嵌套嵌套:一个函数的函数体内,包含一个或多嵌套:一个函数的函数体内,包含一个或多 个函数调用语句个函数调用语句mainint
34、 A()B();int B()int main()A();第56页,本讲稿共83页递归递归一个函数在函数体内直接或间接的调用该函一个函数在函数体内直接或间接的调用该函数本身数本身分为分为直接调用(调用自身)间接调用(A调用B,B调用A)组成组成 递归出口(递归结束条件)递归体 递归计算过程递归计算过程递归函数的计算过程同递归的次序相反!求求n n!f(1)=1;f(n)=n*f(n-1);f(n)=n*f(n-1);第57页,本讲稿共83页例:求n!递归公式 f(n)=n*f(n-1)#include int f(int n)if(n=1)/递归出口递归出口return 1;else /递归体
35、递归体return n*f(n-1);void main()cinn;cout f(n);f(n)f(n)=n*=n*f(n-1)f(n-1)=n*(=n*(n-1)*f(n-2)(n-1)*f(n-2)=n*=n*(n-1)*2*(n-1)*2*f(1)f(1)第58页,本讲稿共83页例:汉诺塔问题例:汉诺塔问题#include void hanio(int,char,char,char);void main()char A=A,B=B,C=C;int n=3;hanio(n,A,B,C);void hanio(int n,char A,char B,char C)if(n=1)cout将第
36、将第n个盘片从个盘片从A柱搬到柱搬到C柱上柱上endl;elsehanio(n-1,A,C,B);cout将第将第n个盘片从个盘片从A柱搬到柱搬到C柱上柱上形参)执行函数体返回调用点并返回要求的函数返回值执行流程执行流程第62页,本讲稿共83页#include struct complexdouble real;double imaginary;int add(int x,int y)return x+y;double add(double x,double y)return x+y;complex add(complex c1,complex c2)complex c;c.real=c1.r
37、eal+c2.real;c.imaginary=c1.imaginary+c2.imaginary;return c;例:根据实参类型选择函数(两数求和)例:根据实参类型选择函数(两数求和)第63页,本讲稿共83页void main()int m=10,n=11;double x=1.1,y=1.2;complex c1,c2,c3;cout add(m,n)endl;cout add(x,y)endl;c1.real=1.0;c1.imaginary=2.0;c2.real=3.0;c2.imaginary=4.0;c3=add(c1,c2);cout(c3.real,c2.imaginar
38、y);根据实参类型选择函数第64页,本讲稿共83页#include int min(int a,int b)return ab?a:b;int min(int a,int b,int c)int t=min(a,b);return min(t,c);int min(int a,int b,int c,int d)int t=min(a,b,c);return min(t,d);void main()cout min(13,5,4,9)endl;cout min(-2,8,0)endl;例:根据实参个数选择函数例:根据实参个数选择函数(求最小值求最小值)第65页,本讲稿共83页注意注意返回类型不
39、能区分函数返回类型不能区分函数float add(int,float);int add(int,float);编译器只根据参数个数和类型来区分同名函数&引用参数不能区分函数引用参数不能区分函数void print(double);void print(double&);void print(const double&);完整例第66页,本讲稿共83页void print(double a)couta;void print(double&a)couta;void print(const double&a)couta;void main()double a;print(a);编译结果出错!编译结果
40、出错!C2668:print:ambiguous call to overloaded function目录第67页,本讲稿共83页4.8#预编译命令预编译命令4.8.1 文件包含命令4.8.2 宏定义命令4.8.3 条件编译命令第68页,本讲稿共83页C+程序的源代码中可包含各种编译指令,这些指令称为预处理命令。虽然它们实际上不是c+语言的一部分,但扩展了c+程序设计的环境。c+提供的预处理命令主要有以下三种:文件包含命令(#include)宏定义命令(#define)条件编译命令 这些命令在程序中都是以“#”来引导,每一条预处理命令必须单独占用一行;由于它不是c+的语句因此一般在结尾没有分
41、号。第69页,本讲稿共83页4.8.1 文件包含命令文件包含命令所谓“文件包含”是指将另一个源文件的内容合并到当前源程序中。格式:#include#include”文件名”到系统提供的指定的目录下面找被包含的文件 “”到用户当前目录下查找被包含文件,建议:系统文件用,用户自定义的用“”若文件不在当前目录内,“”里给出文件路径第70页,本讲稿共83页使用使用include的好处的好处“文件包含文件包含”命令可以命令可以节省节省程序设计人员的程序设计人员的重复劳动。重复劳动。头文件头文件(extern 变量声明,函数声明,宏定变量声明,函数声明,宏定义义)保证所有文件都包含同一个全局变量或函数保证
42、所有文件都包含同一个全局变量或函数的的同一份声明同一份声明,若需要,若需要修改修改声明,则只需改声明,则只需改变变一个一个头文件头文件编译头文件也需要时间,如果文件过大,开编译头文件也需要时间,如果文件过大,开销也大,为降低编译时间的开销,有些销也大,为降低编译时间的开销,有些C实现提供了实现提供了预编译头文件预编译头文件支持支持第71页,本讲稿共83页include注意注意头文件可以包括头文件可以包括(函数原型,宏定义,结构函数原型,宏定义,结构,类定义类定义)一条一条include只能指定包含一个文件,若只能指定包含一个文件,若n个文件,必须用个文件,必须用n条条#include#incl
43、ude#include”student.h”嵌套包含(嵌套包含(A包含包含B,B inlcude C)第72页,本讲稿共83页4.8.2 宏定义命令普通宏定义宏定义命令普通宏定义定义宏#define PI 3.14159其中,#define是宏定义命令,它的作用是将3.14159用PI代替;PI称为宏名。注意:1.空格分割:#define、PI和3.14159间一定要有空格,2.一般宏名定义成大写,以与普通标识符相区别。3.宏被定义后,一般不能再重新定义,必须用#undef 宏名 例:#define PI 3.14159#define PI 3.14/错!重复定义#undef PI /取消宏定
44、义#define PI 3.14 /对!第73页,本讲稿共83页4一个定义过的宏名可以用来定义其他新的宏,但要注意其中的括号,例如:#define WIDTH 80#define LENGTH (WIDTH+10)/宏宏LENGTH 等价于:等价于:#define LENGTH (80+10)注:注:其中的括号不能省略其中的括号不能省略。因为。因为 若求:若求:var=LENGTH*20;/若宏若宏LENGTH定义中有括号,则预处理后变成定义中有括号,则预处理后变成 var=(80+10)*20;/若宏若宏LENGTH定义中没有括号,则预处理后变成定义中没有括号,则预处理后变成 var=80+
45、10*20;第74页,本讲稿共83页带参的宏替换带参的宏替换格式:格式:define(参数表参数表)例:例:#define MAX(a,b)(a)(b)?(a):(b)其中(a,b)是宏MAX的参数表如果在程序中出现语句 x=MAX(3,9);则预处理后变成 x=(39?3:9);/结果为9很显然,带参数的宏相当于一个函数的功能,但比函很显然,带参数的宏相当于一个函数的功能,但比函数要简洁。数要简洁。注意括号尽量不要省!注意括号尽量不要省!第75页,本讲稿共83页思考思考&比较比较define VS.constdefine VS.inline第76页,本讲稿共83页4.8.3 条件编译命令条件
46、编译命令条件编译使得同一个源文件可条件编译使得同一个源文件可根据条件根据条件来决来决定定哪部分哪部分程序参加编译,哪部分不参加编译,程序参加编译,哪部分不参加编译,从而生成不同的目标文件从而生成不同的目标文件 C+提供的条件编译命令形式提供的条件编译命令形式一般情况下,源程序中所有的语句都参加编译,但有时也希望根据一定的条件去编源文件的不同部分,这就是“条件编译”。第77页,本讲稿共83页其中;#ifdef、#else、#elif和#endif都是关键字,和是由若干条预处理命令或语句组成的。含义:如果标识符已(未)被#define命令定义过,则编译,否则编译。形式一:#ifdef#else#e
47、ndif形式二:#ifndef#else#endif形式三:#if#elif#else#endif第78页,本讲稿共83页条件编译用途条件编译用途避免生成不必要的程序代码避免生成不必要的程序代码在调试中加入若干调试代码在调试中加入若干调试代码使程序对不同计算机和操作系统具有可移植使程序对不同计算机和操作系统具有可移植性性第79页,本讲稿共83页ifdef例调试信息#include#define _DEBUG void main()int i;i=i*10;#ifdef _DEBUG cout i endl;/不能与上句同行写#endif第80页,本讲稿共83页多文件引用头文件(出错)多文件引用
48、头文件(出错)/header1.hint gInt;void addd();/header2.h#include header1.h#include header1.h#include header2.h“void main()Compiling.sample1.cppe:sample1header1.h(2):error C2086:gInt:redefinitione:sample1header1.h(3):error C2084:function void _cdecl addd(void)already has a body第81页,本讲稿共83页ifndef多文件引用头文件(正确)多
49、文件引用头文件(正确)/header1.h#ifndef _HEADER1_H#define _HEADER1_Hint gInt;void addd();#endif/_HEADER1_H/header2.h#ifndef _HEADER2_H#define _HEADER2_H#include header1.h/.#endif/_HEADER2_HCompiling.sample1.cppLinking.sample1.exe-0 error(s),0 warning(s)#include header1.h#include header2.h“void main()第82页,本讲稿共83页if elif 例:例:#include#define N 10void main()#if N0 cout 0”endl;#elif N0 cout “N0”endl;#else cout “N=0”endl;#endif目录第83页,本讲稿共83页