第5章
编译预处理
编译预处理是在编译源程序之前,由预处理器对源程序进行一些加工处理工作,如图5.1所示。所谓预处理器,是包含在编译器中的预处理程序。源程序中的编译预处理命令一律以“#”开头,回车符结束,每条命令占一行,并且通常放在源程序文件的开始部分。
图5.1编译预处理过程示意
编译预处理的作用是将源程序文件中的预处理命令进行处理,生成一个中间文件,编译系统再对此中间文件进行编译并生成目标代码,*后生成的目标代码中并不包括预处理命令。
C 提供的预处理功能主要有宏定义、文件包含和条件编译三种。
5.1宏定义
5.1.1不带参数的宏定义
用一个指定的标识符来代表一个字符串,这个指定的标识符称为宏名,格式为:
#define宏名 字符串
宏命令后,凡出现宏名的地方均用其对应的字符串来替换,替换的过程称为宏展开。例如,有以下宏命令:
#definePI3.1415926
则PI为宏名,凡在程序中出现宏名PI的地方均用3.1415926替换。
【例5.1】不带参数的宏替换。
#include<iostream >
#define PI 3.1415926
using namespace std;
int main()
{double area,r,peri;
cout<<"请输入圆的半径: ";
cin>>r;
area=PI* r*r;
peri=2.0*PI*r;
cout<<"圆的面积为: "<<area<<endl;
cout<<"圆的周长为: "<<peri<<endl;
return 0;
}
程序的运行结果如下:
请输入圆的半径: 3↙
圆的面积为: 28.2743
圆的周长为: 18.8496
第
5
章
编译预处理
在上述源程序编译之前,首先执行预编译命令#define PI3.1415926,将源程序内的所有宏名PI都替换成3.1415926,然后再执行正常的编译命令,将源程序转换为目标代码。可见,“宏替换”是一种“机械替换”,因为是在编译之前进行,所以不对宏名(PI)替换的对象(3.1415926)作语法检查,此时的3.1415926不应该看成是实数类型,而认为是一串普通的字符串常量。
特别注意的是,宏定义语句的行末一般不加分号,因为它仅具有替换功能,并不是具体的代码语句。如果行末有分号,那么分号也属于替换对象的一部分,参与宏名的置换,很容易在置换后编译时发生语法错误。例如,例5.1中如果宏定义行末出现分号:
#define PI3.1415926;
源程序中语句area=PI*r*r;经宏展开后变成:
area=3.1415926;*r*r;
该语句在随后的编译过程中显然会出现语法错误。
使用宏定义时,具体说明如下:
(1) 宏展开只是一个简单的“机械”替换,不做语法检查,不是一个语句,其后不加分号“;”。
(2) #define命令出现在函数的外面,其有效范围为定义处至本源文件结束,可以用#undef命令终止宏定义的作用域。例如:
#definePI 3.1415926
doublefun()
{
…
}
#undef PIPI的有效范围
int main()
{
…
}
(3) 在进行宏定义中,可以用已定义的宏名,进行层层置换。
(4) 对程序中用双引号括起来的字符串内容,即使与宏名相同,也不进行置换。