你好,C++(48)每个源文件开头的那些#include到底是几个意思?7.3.4 编译预处理
7.3.4 编译预处理
在前面的例子中,我们见过代码中很多以“#”开始的指令,比如引入头文件的“#include”指令,定义宏的“#define”指令等。这些指令所表达的其实是对源文件的某种处理。当一个源文件编写完成之后,在编译器对其进行编译之前,它还做了一些手脚。编译器会执行其中的这些指令并得到最终的源文件,然后才参与编译。这个过程被称为源文件的预处理过程,相应的,这些指令也被称为预编译指令。
在源文件中加入恰当的预编译指令,可以更好地组织源文件,甚至可以根据条件改变最终参与编译的源文件的内容,达到灵活处理源文件的目的。下面介绍几种最常用的预编译指令。
1. #include 指令
顾名思义,#include指令可以用来将一个文件嵌入(include)到当前位置,实现多个源文件共享同一个文件,共用其中的内容。虽然用#include指令可以嵌入任何文件,但更多时候,它还是被用来嵌入头文件,实现对声明在其中的变量或者函数的引用。#include指令的语法格式如下:
#include <文件名>
#include "文件名"
注意到,这里有两种引入文件名的方式。其中,“<
>”表示按照标准方式在编译器指定的头文件搜索目录下搜索这个文件,而“" "”则表示先在当前目录下搜索,如果当前目录下没有这个文件,然后再按照标准方式搜索。一般来说,用“<
>”来引入系统提供的头文件,比如iostream等,而使用“" "”来引入自己创建的、放置在当前项目文件夹下的头文件,比如Human.h。例如:
// 引入标准程序库中的头文件
#include <iostream>
// 引入自己创建的头文件
#include "Global.h"
2. #define指令和#undef指令
#define指令可以用来定义一个符号常量或者宏,符号常量可以用作条件编译的条件,而宏则可以用来做一些简单的需要不断重复的工作。与之对应的,#undef指令的作用就是删除一个由#define指令定义的符号常量或者宏。例如:
// 定义一个符号常量_DEBUG,用作后面的条件编译
#define _DEBUG
// …
// 在必要的时候,删除_DEBUG符号常量
#undef _DEBUG
3. #ifdef等条件编译指令
一些复杂的C++程序往往需要根据设置或者外部环境条件的不同而编译成不同的版本,比如,最常见的就是根据应用目的的不同将一个程序分别编译成Debug版本和Release版本、带日志输出的版本和不带日志输出的版本等,这时就可以利用条件编译指令,根据不同的条件改变参与编译的源代码内容,从而将同一份程序源代码编译成不同的程序版本。条件编译指令常见的形式如下:
// 条件编译指令开始
#if 常量表达式
// 当常量表达式为true时,本段程序代码参与编译,
// 否则这段代码不参与编译
程序代码
#endif // 表示条件编译结束
#ifdef 标识符
程序段1 // 如果定义了标识符,则编译程序段1
#else
程序段2 // 如果没有定义标识符,则编译程序段2
#endif
#ifndef 标识符
程序段 // 如果没有定义标识符,则编译此程序段
#endif
条件编译指令最常见的用途就是为程序区别编译Debug版本和Release版本,例如下面这个例子:
class Demo
{
// …
public:
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
}
这段代码使用了“#ifdef”预编译指令,它表示如果定义了_DEBUG标识符,就会编译中间这段程序代码,为程序增加两个函数用于辅助调试。而这个标识符只有在在编译Debug版本时采用“#define”指令定义,换句话说,在编译Debug版本时,这段代码会得到编译。而在编译Release版本的时候,并没有定义_DEBUG标识符,这段没必要的代码就不会参与编译,从而减小程序的体积。