📄 c++9.dat
字号:
第九章 编译预处理
第一节 宏定义
我们用C++进行编程的时候,可以在源程序中包括一些编译命令,以告诉编译器对源程序如何进行编译.这些命令包括:宏定义、文件包含和条件编译,由于这些命令是在程序编译的时候被执行的,也就是说,在源程序编译以前,先处理这些编译命令,所以,我们也把它们称之为编译预处理,本章将对这方面的内容加以介绍.
实际上,编译预处理命令不能算是C++语言的一部分,但它扩展了C++程序设计的能力,合理地使用编译预处理功能,可以使得编写的程序便于阅读、修改、 移植和调试.
预处理命令共同的语法规则如下:
* 所有的预处理命令在程序中都是以"#"来引导 如"#include "stdio.h"".
* 每一条预处理命令必须单独占用一行,如"#include "stdio.h" #include <stdlib.h>" 是不允许的.
* 预处理命令后不加分号,如"#include "stdio.h";" 是非法的.
* 预处理命令一行写不下,可以续行,但需要加续行符"\".
下面我们对宏定义、文件包含和条件编译三种预处理命令的用法分别进行介绍.
宏定义命令将一个标识符定义为一个字符串.如:
#define CUBE_THREE 3*3*3
#表示这是一条预处理命令, define为关键字, CUBE_THREE(标识符)称为宏名,也简称为宏, 3*3*3是被定义的字符串.这样, CUBE_THREE就代表字符串3*3*3.当源程序被编译的时候,遇到标识符CUBE_THREE,均以字符串3*3*3进行替换.由于宏定义命令是用于字符串的替换,我们也常把宏名用指定的字符串进行替换的过程,称之为宏替换.
例如:
#include <iostream.h>
//将系统输入输出流头文件包含进来,如果没/有这一行语句,
//用VC编译器进行编译的//话,会出现下面的错误提示:
//error C2065: 'cout' : undeclared identifier
#define CUBE_THREE 3*3*3
//用简单宏定义定义了一个符号常量"CUBE_THREE"
void main()
{
int a;
a=CUBE_THREE;
//将该宏的值赋给a,实际上是将该宏所定义的符号常量的值赋给a,
//因此在编译运//行到该语句的时候,"a=CUBE_THREE;"等价于"a=3*3*3;"
cout<<"a is"<<a<<endl;
//输出a的值,观察宏替换产生的结果
}
编译并运行该程序,结果如下:
a is 27
因为经过宏替换后,语句"a=CUBE_THREE"等价于"a=3*3*3;".
宏替换的功能为我们编程时可以带来一些方便.因为,如果在程序中,某一个常量出现较多,就可以为该常量定义一个宏.这样,假定我们要修改该常量时,就不必在程序中对该常量用手工一个个地查找、修改,而只要修改其宏定义即可.例如,如果在一个程序里,3的立方出现的次数比较多,我们就可以把它定义成上面的宏的形式.假定现在需要把3的立方修改成3的4次方,只要修改宏定义即可:
#define CUBE_THREE 3*3*3*3
宏定义命令通常有两种格式:一种是简单的宏定义,另一种是带参数的宏定义.
1.1 简单的宏定义
上面的实例就是一个简单的宏定义,简单宏定义的一般形式如下:
#define <宏名> <字符串>
其中, define是宏定义命令的关键字,<宏名>是一个标识符,<字符串>可以是常数、表达式、格式串等.
在程序被编译的时侯,如果遇到宏名,先将宏名用指定的字符串替换,然后再进行编译.
1.2 带参数的宏定义
带参数的宏定义的一般形式如下:
#define <宏名>(<参数表>) <宏体>
其中, <宏名>是一个标识符,<参数表>中的参数可以是一个,也可以是多个,视具体情况而定,当有多个参数的时候,每个参数之间用逗号分隔.<宏体>是被替换用的字符串,宏体中的字符串是由参数表中的各个参数组成的表达式.例如:
#define SUB(a,b) a-b
如果在程序中出现如下语句:
result=SUB(2, 3)
则被替换为:
result=2-3;
如果程序中出现如下语句:
result= SUB(x+1, y+2);
则被替换为:
result=x+1-y+2;
在这样的宏替换过程中,其实只是将参数表中的参数代入到宏体的表达式中去,上述例子中,即是将表达式中的a和b分别用2和3代入.
我们可以发现:带参的宏定义与函数类似.如果我们把宏定义时出现的参数视为形参,而在程序中引用宏定义时出现的参数视为实参.那么上例中的a和b就是形参,而2和3以及x+1和y+2都为实参.在宏替换时,就是用实参来替换<宏体>中的形参.
第二节 文件包含
文件包含命令格式如下:
#include <文件名>
或者
#include "文件名"
其中,include是关键字,文件名是指被包含的文件全名.在前面我们已多次用此命令包含过库函数的头文件.例如:"#include "stdio.h"".
文件包含命令的功能是把指定的文件插入该命令行位置,从而把指定的文件和当前的源程序文件连成一个源文件.在C++的程序设计中,文件包含命令用得很多.我们常将符号常量、类及其它类型的定义放到一个.h文件中(即文件扩展名为h的文件,称之为头文件).在其它文件的开头用包含命令包含该头文件,这样就可以减少重复劳动,节省时间,并减少出错的可能性.头文件可以是自己编写的,也可以是系统提供的.例如,前面程序中已出现的iostream.h便是一个系统提供的、有关输入输出操作信息的头文件.
上面我们看到,文件包含命令的格式有两种,一种将文件名以尖括号(< >)括起,另一种是将文件名以双引号("")括起.这两种格式的用法略有区别:使用前一种格式,在编译的时候将会在指定的目录下查找此头文件,而使用后一种格式,在编译的时侯会首先在当前的源文件目录中查找该头文件,若找不到才会到系统的指定目录下去查找.
在定义和使用文件包含时还应注意以下几点:
(1)一条文件包含命令只能包含一个文件,若想包含多个文件须用多条文件包含命令.例如:
#include<iostream.h>
#include<stdio.h>
…
(2)文件包含命令可以嵌套使用,即在一个被包含的文件中可以包含另一个文件.例如,定义一个头文件,其名字为headfilel.h,该文件内容如下:
#include "headfile2.h"
#include "headfile3.h"
…
这里即嵌套使用了文件包含命令.
第三节 条件编译
条件编译命令可以使得编译器按不同的条件去编译程序不同的部分,产生不同的目标代码文件.也就是说,通过条件编译命令,某些程序代码要在满足一定条件下才被编译,否则将不被编译.
常用的条件编译命令有如下三种格式:
3.1 格式一
#ifdef 标识符
程序段1
#else
程序段2
#endif
其中,ifdef、else和endif都是关键字.程序段1和 程序段2是由若干预处理命令和语句组成的.它的功能是,如果标识符已被 #define命令定义过,则对程序段1进行编译;否则对程序段2进行编译.本格式中的#else也可以没有:
#ifdef 标识符
程序段
#endif
3.2 格式二
#ifndef 标识符
程序段1
#else
程序段2
#endif
格式二和格式一形式上的的区别在于ifdef关键字换成了ifndef关键字,其功能是:如果标识符未被#define命令定义过,则对程序段1进行编译, 否则对程序段2进行编译,这与格式一的功能正好相反.例如:
#ifndef NULL
#define NULL ((void *)0)
#endif
本段代码能够保证符号NULL只有一次定义为((void *)0).
3.3 格式三
#if 常量表达式
程序段1
#else
程序段2
#endif
if、else和endif是关键字.程序段1、程序段2都是由若干条预处理命令和语句组成.它的功能是:如常量表达式的值为真(true),则对程序段1 进行编译,否则对程序段2进行编译.因此可以使程序在不同条件下,完成不同的功能.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -