預(yù)處理器是指令,在實際編譯開始之前,預(yù)處理器會給編譯器指令來預(yù)處理需要編譯的信息。
所有的預(yù)處理指令以#
開頭,可能在預(yù)處理指令行之前出現(xiàn)的只有空白字符。預(yù)處理指令是不是 C++ 語句,所以它們不會以分號(;
)結(jié)束。
你已經(jīng)在所有的例子中都看到了 #include 指令。這個宏用于將頭文件包含到源文件。
還有許多 C++ 支持的預(yù)處理指令,如#include
,#define
, #if
, #else
, #line
等等。下面我們來看一些重要的指令:
#define
處理指令用來創(chuàng)建符號常量。這個符號常量被稱為宏。指令的一般形式是:
#define macro-name replacement-text
當(dāng)這一行出現(xiàn)在一個文件中時,在程序被編譯之前,該文件中的所有后續(xù)出現(xiàn)的 macro-name 將會被 replacement-text 替換掉。 例如:
#include <iostream>
using namespace std;
#define PI 3.14159
int main ()
{
cout << "Value of PI :" << PI << endl;
return 0;
}
現(xiàn)在,讓我們對這個代碼進(jìn)行預(yù)處理來看一看結(jié)果。假設(shè)我們有源代碼文件,那么我們用 -E 選項編譯它并重定向結(jié)果到 test.p?,F(xiàn)在,如果你查看 test.p,會發(fā)現(xiàn)里面有大量的信息,并且你會在底部發(fā)現(xiàn)值已經(jīng)被替換了,如下所示:
$gcc -E test.cpp > test.p
...
int main ()
{
cout << "Value of PI :" << 3.14159 << endl;
return 0;
}
你可以用#define
定義一個帶有如下參數(shù)的宏:
#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;
}
如果我們編譯并運(yùn)行上述代碼,將會產(chǎn)生以下結(jié)果:
The minimum is 30
有幾種不同的指令,其可用于有選擇的編譯程序源代碼的一部分。這個過程被稱為條件編譯。
條件預(yù)處理器結(jié)構(gòu)很像 if 選擇結(jié)構(gòu)。思考下面的預(yù)處理代碼:
#ifndef NULL
#define NULL 0
#endif
你可以編譯一個用于調(diào)試的程序,并且可以用單個宏打開或關(guān)閉調(diào)試開關(guān),如下所示:
#ifdef DEBUG
cerr <<"Variable x = " << x << endl;
#endif
如果符號常量 DEBUG 定義在#ifdef DEBUG
指令之前,那么程序中的 cerr 語句會被編譯。你可以使用#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;
}
如果我們編譯并運(yùn)行上述代碼,將會產(chǎn)生以下結(jié)果:
Trace: Inside main function
The minimum is 30
Trace: Coming out of main function
在 C++ 和 ANSI/ISO C 中#
和##
預(yù)處理器操作符是可用的。
#
操作符會將要替代的文本符號轉(zhuǎn)換成用雙引號引起來的字符串。
思考下面的宏定義:
#include <iostream>
using namespace std;
#define MKSTR( x ) #x
int main ()
{
cout << MKSTR(HELLO C++) << endl;
return 0;
}
如果我們編譯并運(yùn)行上述代碼,將會產(chǎn)生以下結(jié)果:
HELLO C++
讓我們來看看它是如何工作的。
這很容易理解,C++ 預(yù)處理器將下面一行代碼:
cout << MKSTR(HELLO C++) << endl;
轉(zhuǎn)變成了下面這一行的代碼:
cout << "HELLO C++" << endl;
##
操作符是用來連接兩個符號的。
例子如下所示:
#define CONCAT( x, y ) x ## y
當(dāng) CONCAT 出現(xiàn)在程序中時,它的參數(shù)會被連接起來,并用其來取代宏。例如,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;
}
如果我們編譯并運(yùn)行上述代碼,將會產(chǎn)生以下結(jié)果:
100
讓我們來看看它是如何工作的。
這很容易理解,C++ 預(yù)處理器將下面一行代碼:
cout << concat(x, y);
轉(zhuǎn)換成了下面這一行的代碼:
cout << xy;
C++ 提供了許多預(yù)定義的宏,如下所示:
宏 | 描述 |
---|---|
__LINE__ | 編譯過后,其包含了當(dāng)前程序行在程序內(nèi)的行號 |
__FILE__ | 編譯過后,其包含了當(dāng)前程序的程序名 |
__DATE__ | 其包含了由源文件轉(zhuǎn)換為目標(biāo)代碼的日期,該日期是格式為 月/日/年 的字符串文本 |
__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;
}
如果我們編譯并運(yùn)行上述代碼,將會產(chǎn)生以下結(jié)果:
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48