在很多程序中,每个源文件都存在一些相同的部分,比如要包括相同的一些头文件,而且这些头文件可能很长。如果用普通的方法编译这些源文件,对这些头文件在每个源文件中的出现都要重新编译,作了很多重复工作。如果将这些头文件专门进行编译,并把结果存储起来,然后在编译包含这些头文件的源文件时,使用上述结果替代头文件在源文件中的出现,就可以大大减少工作量。VC++提供的“预编译头文件”机制就支持这一功能。
所谓的预编译头就是把一个工程中的一部分代码,预先编译好放在一个文件里(通常是以.pch为扩展名的),这个文件就称为预编译头文件。这些预先编译好的代码可以是任何的c/c++代码,甚至是inline函数,但是必须是稳定的,在工程的开发过程中不会被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编译头文件是很耗时的。同时预编译头文件通常很大,及时清理那些没有用的预编译头文件。
也许你会问:现在的编译器都有time stamp的功能,编译器在编译整个工程的时候,它只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还要预编译头文件呢?答案是,我们知道编译器是以文件为单位进行编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有头文件中的东西都要重新处理一遍。
一、常用的预处理功能:
预处理器的主要作用就是把通过预处理的内建功能对一个资源进行等价替换,最常见的预处理有:文件包含,条件编译、布局控制和宏替换4种。
文件包含:#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。
条件编译:#if,#ifndef,#ifdef,#endif,#undef等也是比较常见的预处理,主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。
布局控制:#progma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。
宏替换:
#define,这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。
二、 预处理指令:
预处理指令的格式如下:
# directive
tokens
#符号应该是这一行的第一个非空字符,一般我们把它放在起始位置。如果指令一行放不下,可以通过\进行控制,例如:
#define
Error if(error)
exit(1)
等价于
#define
Error \
if(error) exit(1)
不过我们为了美化起见,一般都不怎么这么用,更常见的方式如下:
# ifdef
__BORLANDC__
if_true<(is_convertible<Value,named_template_param_base>::value)>::
template then<make_named_arg,
make_key_value>::type Make;
# else
enum { is_named =
is_named_parameter<Value>::value
};
typedef typename
if_true<(is_named)>::template
then<make_named_arg,
make_key_value>::type Make;
#
endif
下面我们看一下常见的预处理指令:
#define
宏定义
#undef
未定义宏
#include
文本包含
#ifdef
如果宏被定义就进行编译
#ifndef
如果宏未被定义就进行编译
#endif
结束编译块的控制
#if
表达式非零就对代码进行编译
#else
作为其他预处理的剩余选项进行编译
#elif
这是一种#else和#if的组合选项
#line
改变当前的行数和文件名称
#error
输出一个错误信息