C/C++拾遗之条件编译

条件编译是由一批预处理器指令,通过指定一些限定的条件,有选择性地对源代码进行预处理,从而实现条件编译。后续的编译、链接等过程都是在预处理之后的代码上进行的,所以预处理之后的代码通常要精简很多(这里精简的思想是包含那个引入的头文件的),无意义的编译指令,根据条件编译代码不需要编译的代码,还有注释都会有预处理之后忽略掉。常用的预的处理器指令主要有(方括号中为后面需要跟的内容):

#空指令: 无意义,预处理时会被忽略
#include [file_name]: 包含一个源代码文件,通常是头文件
#define [args] [可选的常量]: 定义宏
#undef [args]:取消已定义的宏
#if [args]: 如果给定条件为真,则编译下面代码,args必须是已定义的常量宏,或者未定义
#ifdef [args]:如果宏已经定义,则编译下面代码
#ifndef [args]:如果宏没有定义,则编译下面代码
#elif [args]:如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif:结束一个#if……#else条件编译块
#error [sentence]:停止编译并显示错误信息
#line [number] [file_name]:修改变量LINEFILE的值

注意有两个特殊的运算符:#运算符和##运算符
下面以代码举例:
头文件:
macro.h

1
2
3
4
5
6
#ifndef MACRO_H  //防止头文件被重复包含
#define MACRO_H
#include <iostream>
# //空指令,没有任何意义

#endif /* !MACRO_H */

源文件:
macro.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "macro.h"

#define PI 3.1415926 //#define指令
#define CUBE(x) (x)*(x)*(x) //带参数的#define指令
#define AGE(x) "My age is " #x //#运算符用于将后面的参数转成字符串
#define CON(x, y, z) x##y##z //##运算符将其两侧的参数合并为一个符号,注意x, y, z必须为数字
#define DEBUG 0
#define RUN 1
#define NODEBUG
#define IS_COMPILE
#undef IS_COMPILE //取消宏定义

int main(void)
{
std::cout << PI << std::endl;
std::cout << CUBE(PI + PI) << std::endl;
std::cout << AGE(25) << std::endl;
std::cout << CON(18, 19, 20) << std::endl;


#if DEBUG //#if后面必须是个常量,如果DEBUG为真,则编译其后的代码,直到#elif,或#else,或#endif
std::cout << "Debug" << std::endl;
#elif RUN //同#if,与控制语句else if作用一样
std::cout << "Run" << std::endl;
#else
std::cout << "NONE" << std::endl;
#endif

#ifdef NODEUBG //等价于#if defined NODEUBG
std::cout << "NoDebug" << std::endl;
#endif

#ifdef IS_COMPILE
std::cout << "is compile" << std::endl;
#endif

#ifndef NODEUBG //等价于#if !defined NODEUBG
std::cout << "no NoDebug" << std::endl;
#endif

#line 15 "my_macro.cpp" //更改当前行为第15行,文件名为my_macro.cpp,也就是修改__LINE__和__FILE__变量的值

#if !defined(__cplusplus) //如果没有定义__cplusplus则报错误#error+其后的字符串内容
#error C++ compiler required.
#endif

return 0;
}

上述代码通过g++ 4.8.4预处理(命令是g++ -E -std=c++11 macro.cpp)之后的代码如下(忽略掉包含的头文件iostream部分):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 1 "macro.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "macro.cpp"


# 1 "macro.h" 1
# 10 "macro.h"
# 1 "/usr/include/c++/4.8/iostream" 1 3
# 36 "/usr/include/c++/4.8/iostream" 3

# 37 "/usr/include/c++/4.8/iostream" 3

# 1 "/usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h" 1 3
# 184 "/usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h" 3


# 11 "macro.h" 2
# 9 "macro.cpp" 2
# 18 "macro.cpp"
int main(void)
{
std::cout << 3.1415926 << std::endl;
std::cout << (3.1415926 + 3.1415926)*(3.1415926 + 3.1415926)*(3.1415926 + 3.1415926) << std::endl;
std::cout << "My age is " "25" << std::endl;
std::cout << 181920 << std::endl;

std::cout << "Run" << std::endl;
# 38 "macro.cpp"
std::cout << "no NoDebug" << std::endl;
# 15 "my_macro.cpp"

return 0;
}

可以看到代码中少了很多预处理器不需要处理的代码,而且里面已经没有任何预处理指令了,所有剩下的代码都是必须编译并执行的代码。而且return 0的前面一句显示出行号和文件名被#line改成了15、my_macro.cpp