C++预处理器
预处理器指令是在实际编译开始之前给编译器的指示,用于预处理信息。
所有的预处理器指令以#开头,在预处理器指令前只能出现空白字符。预处理器指令不是C++语句,所以不以分号(;)结尾。
在所有的示例中,你已经见过一个 #include 指令。这个宏用于将头文件包含到源文件中。
C++支持多种预处理器指令,如#include,#define,#if,#else,#line等。让我们看一下重要的指令−
#define
预处理器
# define
预处理器指令用于创建符号常量。符号常量被称为 宏 ,指令的一般形式是−
#define macro-name replacement-text
当这行出现在文件中时,在该文件中的所有后续的宏的出现都会在程序编译之前被替换为替代文本。例如 –
#include <iostream>
using namespace std;
#define PI 3.14159
int main () {
cout << "Value of PI :" << PI << endl;
return 0;
}
现在,让我们对这段代码进行预处理,假设我们有源代码文件。所以让我们使用-E选项进行编译,并将结果重定向到test.p。现在,如果你检查test.p,你会发现它有很多信息,在底部,你会发现值被替换如下 –
$gcc -E test.cpp > test.p
...
int main () {
cout << "Value of PI :" << 3.14159 << endl;
return 0;
}
函数样式宏
您可以使用#define来定义一个宏,它将按照以下方式接受参数 –
#include <iostream>
using namespace std;
#define MIN(a,b) (((a)<(b)) ? a : b)
int main () {
int i, j;
i = 100;
j = 30;
cout <<"The minimum is " << MIN(i, j) << endl;
return 0;
}
如果我们编译并运行上述代码,将会产生以下结果:
The minimum is 30
条件编译
有几个指令可以用来编译程序源代码的选择性部分。这个过程被称为条件编译。
条件预处理器结构非常类似于’if’选择结构。考虑以下预处理器代码 –
#ifndef NULL
#define NULL 0
#endif
您可以为了调试目的而编译程序。您还可以使用以下单个宏来打开或关闭调试。
#ifdef DEBUG
cerr <<"Variable x = " << x << endl;
#endif
这将导致在程序中使用 cerr 语句进行编译,前提是在#ifdef DEBUG指令之前已经定义了符号常量DEBUG。 您可以使用#if 0语句来注释掉程序的一部分,如下所示 –
#if 0
code prevented from compiling
#endif
让我们尝试以下示例:
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main () {
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr <<"Trace: Inside main function" << endl;
#endif
#if 0
/* This is commented part */
cout << MKSTR(HELLO C++) << endl;
#endif
cout <<"The minimum is " << MIN(i, j) << endl;
#ifdef DEBUG
cerr <<"Trace: Coming out of main function" << endl;
#endif
return 0;
}
如果我们编译并运行以上代码,将会产生以下结果−
The minimum is 30
Trace: Inside main function
Trace: Coming out of main function
#
和 ##
运算符
#
和 ##
预处理器运算符可以在 C++ 和 ANSI/ISO C 中使用。# 运算符导致替换文本标记被转换为由引号包围的字符串。
考虑以下宏定义 –
#include <iostream>
using namespace std;
#define MKSTR( x ) #x
int main () {
cout << MKSTR(HELLO C++) << endl;
return 0;
}
如果我们编译并运行上述代码,将会产生以下结果 −
HELLO C++
让我们看看它是如何工作的。很容易理解的是,C++预处理器将这行代码转换为-
cout << MKSTR(HELLO C++) << endl;
上面的行将变成下面的行-
cout << "HELLO C++" << endl;
##
运算符用于连接两个记号。这是一个示例 –
#define CONCAT( x, y ) x ## y
当程序中出现CONCAT时,它的参数会被连接在一起,并用于替换宏。例如,CONCAT(HELLO, C++)会在程序中被替换为”HELLO C++”,如下所示。
#include <iostream>
using namespace std;
#define concat(a, b) a ## b
int main() {
int xy = 100;
cout << concat(x, y);
return 0;
}
如果我们编译和运行上面的代码,将会产生以下结果 −
100
让我们来看看它是如何工作的。我们很容易理解,C++预处理器会进行以下转换−
cout << concat(x, y);
以上行将转换为以下行−
cout << xy;
预定义的C++宏
C++提供了一些预定义的宏,如下所示:
序号 | 宏 & 描述 |
---|---|
1 | __LINE__ 这个宏包含了程序在编译时的当前行号。 |
2 | __FILE__ 这个宏包含了程序在编译时的当前文件名。 |
3 | __DATE__ 这个宏包含了一个格式为月/日/年的字符串,表示源文件转换为目标代码的日期。 |
4 | __TIME__ 这个宏包含了一个格式为小时:分钟:秒钟的字符串,表示程序编译的时间。 |
让我们来看一个关于上述所有宏的示例 –
#include <iostream>
using namespace std;
int main () {
cout << "Value of __LINE__ : " << __LINE__ << endl;
cout << "Value of __FILE__ : " << __FILE__ << endl;
cout << "Value of __DATE__ : " << __DATE__ << endl;
cout << "Value of __TIME__ : " << __TIME__ << endl;
return 0;
}
如果我们编译并运行上述代码,将会得到以下结果 –
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48