的,不对实参产生影响如果想要实参值改变,不如果想要实参值改变,不能用传值方法能用传值方法形参及形参及变量局变量局部有效部有效2. 地址传递§是指在函数定义时是指在函数定义时将形参说明成指针将形参说明成指针§调用函数时就需要指定调用函数时就需要指定地址值形式的实参地址值形式的实参§地址传递方式的特点是可以通过改变形参地址传递方式的特点是可以通过改变形参((指针指针)所指向的变量来影响实参所指向的变量来影响实参#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=2这说明通过这说明通过指针变指针变量的形参可以实现量的形参可以实现函数的地址传递函数的地址传递(有关指针内容将在(有关指针内容将在第五章介绍)第五章介绍)3.引用传递§引用类型引用类型•引用是给已知变量起一个别名引用是给已知变量起一个别名•当建立引用时,程序用另一个变量的名字来对其进当建立引用时,程序用另一个变量的名字来对其进行初始化。
行初始化•引用的声明形式:引用的声明形式:<类型类型> &引用名引用名=变量名变量名•对引用的操作实际是对其引用的变量的操作对引用的操作实际是对其引用的变量的操作int a,b=10;int &i=a;i=5; i=b;•引用在声明时必须初始化引用在声明时必须初始化•引用一旦声明引用一旦声明,就不能再成为其他变量的别就不能再成为其他变量的别名名•引用与被引用的变量指向同一内存地址引用与被引用的变量指向同一内存地址a,,i……例如:例如:void swap(int & x, int & y){…}§按引用传递按引用传递•在函数定义时将形参前面加上引用运算符在函数定义时将形参前面加上引用运算符“&”•实参传递为同类型的变量形参成为实参的实参传递为同类型的变量形参成为实参的引用•形参的任何操作都会改变相应的实参的数据形参的任何操作都会改变相应的实参的数据#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=2函数函数swap中的中的int &x和和int &y就是形参的引用就是形参的引用说明。
说明•引用经常用来做引用经常用来做函数形参和函数的返回值函数形参和函数的返回值在运算符重载中也经常用到引用)(在运算符重载中也经常用到引用)使用引用的目的在于使用引用的目的在于§需要改变函数的实参值需要改变函数的实参值§大类型对象做函数参数大类型对象做函数参数§当一个函数返回一个引用时,它可以成为左当一个函数返回一个引用时,它可以成为左值4.3.4 函数的默认参数值§在在C++中,允许在函数的说明或定义时给一中,允许在函数的说明或定义时给一个或多个参数指定默认值调用时可以不给个或多个参数指定默认值调用时可以不给出参数,而按指定的默认值进行工作出参数,而按指定的默认值进行工作定义定义定义定义void fun( void fun( intint x, x, intint y = 0 y = 0 ) ){…{…} }调用调用调用调用fun(1);fun(1,,0); fun(1,,1);注意:注意:1.可以可以全部全部或或部分部分参数定义为带默认值,但参数定义为带默认值,但带默认值的参数只能放在参数表的带默认值的参数只能放在参数表的最后最后 (从右至左逐渐定义)(从右至左逐渐定义) •因为因为系统进行参数匹配时是依照从前往后系统进行参数匹配时是依照从前往后(从左往右)的顺序,(从左往右)的顺序,如果中间参数有默认值,如果中间参数有默认值,它就无法判断哪些参数使用默认值。
它就无法判断哪些参数使用默认值f(5);f(3,6);f(2,3,4);•例例:以下函数定义以下函数定义不正确不正确:: void f ( int p1=1,,int p2,,int p3=3 ) {…} void f ( int p1=1,,int p2=2,,int p3 ) {…} void f ( int p1, int p2=2, int p3){…}2.函数又有定义,又有说明时,一般在说函数又有定义,又有说明时,一般在说明中给出默认形参值明中给出默认形参值void f (int x, int y=0);void main(){ }void f (int x, int y){}3.程序还可以程序还可以通过重新定义函数原型使本来不带默认值通过重新定义函数原型使本来不带默认值的参数带上默认值的参数带上默认值,这是使通用函数特定化的有效方,这是使通用函数特定化的有效方法法但在同一作用域中,函数的某一参数的默认值只能说但在同一作用域中,函数的某一参数的默认值只能说明一次void f( int x, int y=0 ); void f( int x=1, int y ); //合法合法 void f (int x, int y=1); //不可不可 void main() {void f (int x, int y=1);//可以可以 } void f (int x, int y) { }#include void f(int x,int y=0);void f(int x=1,int y);void main( ){ f( ); f(0); f(1,1);}void f(int x,int y){ cout << “x=” <
•变量的作用域分为三类:变量的作用域分为三类:文件域、局部域和类域文件域、局部域和类域变量定义位置有效范围文件域 程序文件中除函数和程序文件中除函数和类定义以外的部分类定义以外的部分从说明开始到文件结束从说明开始到文件结束局部域 块内函数,或复块内函数,或复合语句内部)合语句内部)从定义到块结束函从定义到块结束函数或复合语句)数或复合语句)类域类内类属性)类内类属性)类内类内#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作用域作用域#incledeint 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);}§同一作用域,不能声明同名的标志符同一作用域,不能声明同名的标志符§在没有互相包含关系的作用域中,声明的标在没有互相包含关系的作用域中,声明的标志符互不影响志符互不影响§具有包含关系的作用域中声明了同名标志符,具有包含关系的作用域中声明了同名标志符,则外层标志符在内层不可见则外层标志符在内层不可见#include int n=1;; //n为全局变量为全局变量void Fun(){ int n=10; // n为函数内局部变量为函数内局部变量 cout <<“Fun:n=” << n << endl;}void main(){ n++; // n为全局变量为全局变量 Fun(); if (n>0) { int n=5; //n为块内局部变量为块内局部变量 cout <<“Block:n=”<
值只能说明一次void initialize(int printNO, int state=0);void initialize(int printNO, int state=1);//不可不可void main(){void initialize(int printNO, int state=1);//可以可以}void initialize(int printNO, int state){}struct stu{};void f(){stu s1;}void main(){stu s2;}void f(){struct stu{};stu s1;}void main(){stu s2;//?}4.4.1 永久变量、临时变量和静态变量变量的存储空间与生命期变量的存储空间与生命期Ü变量的存储空间变量的存储空间Ü可以是可以是永久永久的(即在的(即在程序运行期间该变量一程序运行期间该变量一直存在)直存在)Ü也可以是也可以是暂时暂时的(即的(即变量在程序运行到达其变量在程序运行到达其定义处时才会产生,而定义处时才会产生,而作用域结束时,变量也作用域结束时,变量也随之消亡)随之消亡)全局变量是永久全局变量是永久的,它们在程序的,它们在程序运行的过程中一直存在;运行的过程中一直存在;而而局部变量大多是临时局部变量大多是临时的,它们的,它们只在说明它们的作用域内发挥作只在说明它们的作用域内发挥作用,一旦程序控制离开了这一作用,一旦程序控制离开了这一作用域,这些局部变量所占空间就用域,这些局部变量所占空间就会释放。
会释放静态变量(static)§静态局部变量静态局部变量•声明方式:在局部变量定义前加上声明方式:在局部变量定义前加上static标记 static int local;•既有在局部域作用的特性,又可以永久存在既有在局部域作用的特性,又可以永久存在•系统给静态的局部变量分配固定的存储空间,而系统给静态的局部变量分配固定的存储空间,而不是每次执行到该局部域时才分配空间,所以它不是每次执行到该局部域时才分配空间,所以它能一直保持值能一直保持值•静态局部变量只在第一次执行时初始化一次,一静态局部变量只在第一次执行时初始化一次,一般的局部变量每次执行到该局部域都需要初始化般的局部变量每次执行到该局部域都需要初始化int add(){static int sum=0;sum++;return sum;}void main(){for(int j=0;j<10;j++){cout<int global1=0;; //全局变量可以在程序中显式初始化全局变量可以在程序中显式初始化int global2; //全局变量也可以由系统隐式初始化为全局变量也可以由系统隐式初始化为0static int global3=1 ; //静态全局变量静态全局变量void main(){int i ; //局部临时变量局部临时变量for(i=0;;i<3;;i++){ int temp=l; //局部临时变量,每次循环都会重新初始化局部临时变量,每次循环都会重新初始化 static int perm=l; //静态局部变量,只在第一次初始化静态局部变量,只在第一次初始化 cout << “temp=”<< temp << “,perm=”perm <
•静态全局变量的含义完全不同于静态局部变量:静态全局变量的含义完全不同于静态局部变量:被定义为静态的全局变量被定义为静态的全局变量只在定义它的文件中可只在定义它的文件中可见,其他文件中不能使用见,其他文件中不能使用•静态全局变量有两个好处:静态全局变量有两个好处:§一是一是信息隐藏信息隐藏,,§二是可以二是可以在不同的文件中使用意义不同而同名的变量在不同的文件中使用意义不同而同名的变量名4.4.2 域运算符§局部变量可以局部变量可以隐藏全局变量隐藏全局变量,那么在有同名全局和,那么在有同名全局和局部变量的情形时如何访问全局变量呢局部变量的情形时如何访问全局变量呢?§域运算符域运算符“::::”可以可以提供对全局变量的访问提供对全局变量的访问以域运算符为前缀的变量表示全局变量域运算符为前缀的变量表示全局变量int var=10 ;; //全局变量全局变量func(){ int var;; //局部变量局部变量 var=:: var;; //将全局变量的值赋给局部变量将全局变量的值赋给局部变量}4.4.3.外部变量(extern)§外部变量说明外部变量说明extern标识:标识: extern int var;§和变量相同,定义在一个文件中的函数也可和变量相同,定义在一个文件中的函数也可以由另一个文件引用:以由另一个文件引用:extern void func(int,int);定义文件定义文件1中的中的全局变量全局变量可以由另一个文件可以由另一个文件(文件文件2)引用,但需引用,但需要在文件要在文件2中说明一下所要引用的变量,该变量对于文件中说明一下所要引用的变量,该变量对于文件2来说来说是外部变量。
是外部变量注:如果在说明外部变量时不能给变量赋初值注:如果在说明外部变量时不能给变量赋初值#include void f() { int b; b=a*2; //错误错误printf("%d\n",b);}int a=100; main() { f(); a++; f();}#include void f() { int b; extern int a; b=a*2; printf("%d\n",b); } int a=100; main() { f(); a++; f(); } 在在f内内部部增增加加 extern int a;; 将将全全局局变变量量 a的的 作作 用用域域扩扩展展到到函函数数f的的内内部部→→/* p1.c */#include int a[10]={0}; void main(){……}/* p2.c */#include extern int a[10]; //也可以这样写:也可以这样写:extern int a[ ];void getdata(){ int k; for(k=0;k<10;k++) scanf("%d",&a[k]); }4.4.4.自动变量自动变量(auto)和寄存器变量(和寄存器变量(register)1、自动变量、自动变量§说明加上关键字说明加上关键字auto:: auto int n;§函数内没说明存储类型的变量都是函数内没说明存储类型的变量都是auto的,只是省的,只是省略了略了auto。
§一般用一般用自动存储类型自动存储类型说明的变量都限制在某个程序说明的变量都限制在某个程序范围内使用,即为范围内使用,即为局部变量(包括函数形参)局部变量(包括函数形参)§当程序执行到超出该变量的作用域时,就释放他所当程序执行到超出该变量的作用域时,就释放他所占用的内存空间,其值也随之消失了占用的内存空间,其值也随之消失了2、寄存器变量、寄存器变量§使用关键字使用关键字register说明§寄存器类型的变量的目的是寄存器类型的变量的目的是将说明的变量将说明的变量放入寄存器内放入寄存器内,从而,从而加快了程序的运行速加快了程序的运行速度§但有时在使用这种说明时,若但有时在使用这种说明时,若系统寄存器系统寄存器已经被其它数据占用,寄存器类型的变量已经被其它数据占用,寄存器类型的变量就会自动当作就会自动当作auto变量 堆区堆区 (动态数据动态数据)栈区栈区(函数局部数据)(函数局部数据)全局数据区全局数据区 (全局变量、静态变量全局变量、静态变量)代码区代码区 (程序代码)(程序代码)操操作作系系统统为为一一个个C++C++程程序序的的运运行行所所分分配配的的内内存存分分为为四四个区域,如图个区域,如图程序在内存中的区域所示:程序在内存中的区域所示:C++的内存布局代码区代码区:存放程序代码:存放程序代码全局数据区全局数据区:存放程序的:存放程序的全局数据和静态数据,区全局数据和静态数据,区分配时全部内存清分配时全部内存清0栈区栈区:存放程序中的局部:存放程序中的局部变量,区分配时不处理变量,区分配时不处理堆区堆区:存放与指针相关的:存放与指针相关的动态数据,区分配时不处动态数据,区分配时不处理理静态生命期静态生命期•指的是标识符从程序开始运行时存在,即具有存储指的是标识符从程序开始运行时存在,即具有存储空间,到程序运行结束时消亡,即释放存储空间。
空间,到程序运行结束时消亡,即释放存储空间•具有静态生命期的标识符存放在具有静态生命期的标识符存放在全局数据区全局数据区,如全,如全局变量、静态全局变量、静态局部变量局变量、静态全局变量、静态局部变量•具有静态生命期的标识符在未被用户初始化的情况具有静态生命期的标识符在未被用户初始化的情况下,系统会下,系统会自动将其初始化为自动将其初始化为0 0 函数驻留在代码区,也具有静态生命期所有具函数驻留在代码区,也具有静态生命期所有具有有文件作用域文件作用域的标识符都具有静态生命期的标识符都具有静态生命期变量的生命期变量的生命期局部生命期局部生命期•在在函数内部函数内部或或块块中定义的标识符具有局部生命期,中定义的标识符具有局部生命期,其生命期开始于执行到该函数或块的标识符声明处,其生命期开始于执行到该函数或块的标识符声明处,结束于该函数或块的结束处结束于该函数或块的结束处•具有局部生命期的标识符存放在具有局部生命期的标识符存放在栈区栈区具有局部生具有局部生命期的标识符如果未被初始化,其命期的标识符如果未被初始化,其内容是随机的内容是随机的 具有局部生命期的标识符必定具有局部作用域;具有局部生命期的标识符必定具有局部作用域;但反之不然,但反之不然,静态局部变量具有局部作用域,但却具静态局部变量具有局部作用域,但却具有静态生命期。
有静态生命期动态生命期动态生命期•具有动态生命期的标识符具有动态生命期的标识符由特定的函数调用或运算由特定的函数调用或运算来创建和释放来创建和释放,如调用,如调用mallocmalloc()()或用或用newnew运算符为变运算符为变量分配存储空间时,变量的生命期开始,而调用量分配存储空间时,变量的生命期开始,而调用freefree()()或用或用deletedelete运算符释放空间或程序结束时,变量生运算符释放空间或程序结束时,变量生命期结束命期结束•具有动态生命期的变量存放在具有动态生命期的变量存放在堆区堆区•具有动态生命期的标识符如果未被初始化,其具有动态生命期的标识符如果未被初始化,其内容内容是随机的是随机的•(关于(关于newnew运算和运算和deletedelete运算将在后面章节介绍)运算将在后面章节介绍) 自动自动变量变量静态变量静态变量 局部局部 全局全局寄存器寄存器变量变量外部变量外部变量(全局变量)(全局变量)程序结束前程序结束前一直保存;一直保存;(生命期)(生命期)多个函数共多个函数共享;(作用享;(作用域)域)初始化;初始化; 否否 否否不定不定有有 有有否否 可以可以 0 0 无无 否否不定不定 有有 可以可以 0变量的存储类型:变量定义时可以加上关键字,标识变量的变量的存储类型:变量定义时可以加上关键字,标识变量的存储类型,系统根据其定义分配释放存储空间。
存储类型,系统根据其定义分配释放存储空间存储类型关键字存储类型关键字 类型名类型名 变量名表;变量名表;4.5 内联函数§内联内联(inline)是是内联扩展内联扩展(inline expansion)的简称§为什么引入内联函数?为什么引入内联函数?§在编译时,在编译时,C十十编译器在遇到调用内联函数十十编译器在遇到调用内联函数的地方会的地方会用函数体中的代码来替换函数的调用函数体中的代码来替换函数的调用用§好处:好处:节省节省函数调用带来的参数传递、运行函数调用带来的参数传递、运行栈的入栈与出栈等栈的入栈与出栈等开销开销,,从而提高运行速度从而提高运行速度§代价:代价:增加了代码长度增加了代码长度一般函数调用机制main( ){...调调func()...}保存:返回地址保存:返回地址当前现场当前现场恢复:主调函数现场恢复:主调函数现场返回地址返回地址void func( ){...}定义定义:在一般函数定义前加在一般函数定义前加inline关键字inline int abs(int x){ if (x<0) 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);转换代码 if(m1<0) m=-m1; else m=m1; //n=abs(n1);转换代码 If (nl<0) n=-n1; else n=nl; …} inline对编译器来说只是一个建议对编译器来说只是一个建议,编译器可以选择忽编译器可以选择忽略该建议略该建议内联函数的使用有一些限制:内联函数的使用有一些限制:•内联函数一般是比较小的、经常被调用的函数。
内联函数一般是比较小的、经常被调用的函数•内联函数不能是递归的内联函数不能是递归的•内联函数中不能定义任何静态变量内联函数中不能定义任何静态变量•内联函数一般不能使用循环、内联函数一般不能使用循环、switch和和goto语句语句,内内联函数中不能说明数组联函数中不能说明数组4.6递归函数§递归调用:函数可以调用其自身•直接递归void f(){ …f();…}•间接递归void f1(){f2();}void f2(){f1();}递归调用的终止条件!递归调用的终止条件!例1:求n!n!=n*(n-1)*…*1;n!=n*(n-1)!1!=1;f(n)=n*f(n-1);f(1)=1;long f (int n){if(n= = 1)return 1;elsereturn n*f(n-1);}void main(){int n; long h;printf(“input n(n>=1):”);scanf(“%d”,&n);h=f(n);printf(“%d!=%ld”,n,h);}例2:梵塔问题设设3根柱子根柱子A、、B、、C,,n个盘片个盘片盘片从盘片从A柱搬到柱搬到C柱柱构造递归关系:构造递归关系:f(n,a,b,c):移动:移动n个盘片,个盘片,从从a借助借助b到到cf(n,a,b,c)与与f(n-1, …)的递归的递归关系?关系?f(n-1,a,c,b)move(a,c)f(n-1,b,a,c)void move(char m,char n){printf(“%c%c”,,m,n);}void f( int n,,char a,,char b,,char c){if(n==1){move(a,c);return;}f(n-1,a,c,b);move(a,c);f(n-1,b,a,c);}void main(){f(3,‘A’,’B’,’C’);}4.7 函数重载§所谓所谓函数重载函数重载是指是指同一个函数名可以对应多同一个函数名可以对应多个函数的实现。
个函数的实现§作用:作用:§如何区分?如何区分?•函数重载要求编译器能够惟一地确定调用一个函函数重载要求编译器能够惟一地确定调用一个函数时应执行哪个函数代码数时应执行哪个函数代码,确定函数实现时,要,确定函数实现时,要求从函数参数的求从函数参数的个数个数和和类型类型上来区分上来区分 这就是说,进行函数重载时,要求这就是说,进行函数重载时,要求同名同名函数在参数个数上不同,或者参数类型函数在参数个数上不同,或者参数类型上不同上不同否则,将无法实现重载否则,将无法实现重载#include 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.real+c2.real;c.imaginary=c1.imaginary+c2.imaginary;return c;}struct complex{double real;double imaginary;}void main(){int m=10, n=11;double x=1.1, y=1.2;cout <int min(int a,int b){ return a
虽然它们实际上不是虽然它们实际上不是c++语言语言的一部分,但扩展了的一部分,但扩展了c++程序设计的环境程序设计的环境§c++提供的预处理命令主要有以下三种:提供的预处理命令主要有以下三种: · 宏定义命令宏定义命令 (#define) · 文件包含命令文件包含命令(#include) · 条件编译命令条件编译命令 这些命令在程序中都是以这些命令在程序中都是以“#”来引导,每一条预来引导,每一条预处理命令必须处理命令必须单独占用一行单独占用一行;由于它不是;由于它不是c++的语的语句因此一般在句因此一般在结尾没有分号结尾没有分号“;”4.8.1 宏定义命令§定义宏定义宏#define PI 3.14159其中,其中,#define是宏定义命令是宏定义命令,它的作用它的作用是将是将3.14159用用PI代替;代替;PI称为称为宏名宏名§需要注意的是:需要注意的是:1.#define、、PI和和3.14159之间一定要有空格,之间一定要有空格,且一般将宏名定义成大写,以与普通标识符且一般将宏名定义成大写,以与普通标识符相区别。
相区别2.宏被定义后,一般不能再重新定义,而仅当宏被定义后,一般不能再重新定义,而仅当使用如下命令时才可以:使用如下命令时才可以:#undef 宏名宏名3.一个定义过的宏名可以用来定义其他新的宏,一个定义过的宏名可以用来定义其他新的宏,但要注意其中的括号但要注意其中的括号,例如:,例如:#define WIDTH 80#define LENGTH (WIDTH+10) //#define LENGTH (80+10)注:注:其中的括号不能省略其中的括号不能省略 var = LENGTH * 20 ; var = ( 80 + 10 ) * 20; //若宏若宏LENGTH定义中有括号定义中有括号 var = 80 + 10 * 20; //若宏若宏LENGTH定义中没有括号定义中没有括号4.上述宏定义是一种简单的形式,实际上它还可上述宏定义是一种简单的形式,实际上它还可以带参数以带参数,例如:,例如:#define MAX(a,b) ( (a)>(b) ? (a) : (b) )其中(其中(a,b))是宏是宏MAX的参数表的参数表如果在程序中出现语句如果在程序中出现语句 x=MAX(3,9);则预处理后变成则预处理后变成 x=(3>9 ? 3:9); //结果为结果为9很显然,很显然,带参数的宏相当于一个函数的功能,但比带参数的宏相当于一个函数的功能,但比函数要简洁函数要简洁。
4.8.2 文件包含命令§所谓所谓“文件包含文件包含”是指是指将另一个源文件的内容将另一个源文件的内容合并到当前源程序中合并到当前源程序中§C++语言提供语言提供#include命令用来实现:命令用来实现: #include <文件名文件名> #include ”文件名文件名”格式一:用来包含那些由格式一:用来包含那些由系统系统提供的并放在提供的并放在指指定子目录定子目录中的中的头文件头文件格式二:用来包含那些由格式二:用来包含那些由用户用户自己定义的放在自己定义的放在当前目录或其他目录下的文件当前目录或其他目录下的文件§include指令可以嵌套使用指令可以嵌套使用“文件包含文件包含”命令可以命令可以节省程序设计人员的重复劳动节省程序设计人员的重复劳动头文件头文件::extern 变量声明,函数声明,宏定义变量声明,函数声明,宏定义...•保证所有文件都包含同一个全局变量或函数的同一保证所有文件都包含同一个全局变量或函数的同一份声明,如果需要修改声明,则只需改变一个头文份声明,如果需要修改声明,则只需改变一个头文件件•头文件提供的声明逻辑上应该属于一个组头文件提供的声明逻辑上应该属于一个组•编译头文件也需要时间,如果文件过大,开销也大,编译头文件也需要时间,如果文件过大,开销也大,为降低编译时间的开销,有些为降低编译时间的开销,有些C++实现提供了预++实现提供了预编译头文件支持编译头文件支持4.8.3 条件编译命令§条件编译使得条件编译使得同一源同一源程序在不同的编译条程序在不同的编译条件得到不同的目标代件得到不同的目标代码。
码§ C++提供的条件编译提供的条件编译命令形式命令形式一般情况下,源程序一般情况下,源程序中所有的语句都参加中所有的语句都参加编译,但有时也希望编译,但有时也希望根据一定的条件去编根据一定的条件去编源文件的不同部分,源文件的不同部分,这就是这就是“条件编译条件编译”其中;其中;#ifdef、、#else和和#endif都是关键字,都是关键字,<程序程序段段1>和和<程序段程序段2>是由若干条预处理命令或语句组是由若干条预处理命令或语句组成的含义含义:如果标识符己被如果标识符己被#define命令定义过,则编译命令定义过,则编译<程序程序段段1>,否则编译,否则编译<程序段程序段2>形式一:形式一: #ifdef<标识符标识符> <程序段程序段1> [#else <程序段程序段2>] #endif标识符为宏名标识符为宏名(用宏名做为(用宏名做为编译条件)编译条件)形式一形式一 例:例:#include#define N 10void main(){ #ifdef N cout <“N已定义已定义:N=” << N << endl;; #else cout<< ”N未定义未定义”< <程序段程序段1>[#else <程序段程序段2>]#endif这与前一种形式的区别仅在于这与前一种形式的区别仅在于:如果标识符没如果标识符没有被有被#define命令定义过,则就编译命令定义过,则就编译<程序段程序段1>,否则编译,否则编译<程序段程序段2>。
标识符为宏名标识符为宏名(用宏名做为(用宏名做为编译条件)编译条件)形式三:形式三:#if<表达式表达式1><程序段程序段1>[#elif<表达式表达式2><程序段程序段2>][#else<程序段程序段n>]#endif其中其中,#if,#elif,#else和和#endif是关键字是关键字.含义是:含义是:如果如果<表达式表达式1>为为true就编译就编译<程序段程序段1>,否则如果否则如果<表达式表达式2>为为true就编译就编译<程序段程序段2>,…如果各表达式都不如果各表达式都不true就编译就编译<程序段程序段n>用表达式做为用表达式做为编译条件编译条件形式三 例:#include#define N 10void main(){ #if N>0 cout << “N>0” << endl; #elif N<0 cout << “N<0” << endl; #else cout << “N=0” << endl; #endif}4.8.4 断言§assert宏§assert.h头文件定义§测试表达式的值:如果表达式值为0,打印错误信息,并调用abort()以结束程序assert(x<10);#include #include void main(){int x;cout<<“x:”;cin>>x;assert(x<10);cout<<“x=“<#include #define NDEBUGvoid main(){int x;cout<<“x:”;cin>>x;assert(x<10);cout<<“x=“<