預處理器指令

預處理器指令是指程式程式碼中包含的行,它們不是程式語句,而是給預處理器的指令。這些行總是以井號(##)開頭。預處理器在實際編譯程式碼開始之前執行,因此預處理器會在生成任何程式碼之前處理所有這些指令。

這些預處理器指令僅擴充套件到一行程式碼。一旦找到換行符,預處理器指令就被認為結束。預處理器指令的末尾不需要分號(;)。預處理器指令可以透過在行尾的換行符前加上反斜槓(\).

宏定義 (#define, #undef)

要定義預處理器宏,我們可以使用#define。它的格式是

#define 識別符號 替換內容

當預處理器遇到此指令時,它會將程式碼其餘部分中任何出現的識別符號替換為替換內容。這個替換內容可以是表示式、語句、程式碼塊或任何東西。預處理器不理解 C++,它只是簡單地替換任何出現的識別符號替換內容.

1
2
3
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
int table2[TABLE_SIZE];

在預處理器替換了TABLE_SIZE之後,程式碼等效於

1
2
int table1[100];
int table2[100];

我們已經從之前的教程中瞭解到使用 #define 作為常量定義器,但是#define也可以使用引數來定義函式宏

1
#define getmax(a,b) a>b?a:b 

這將替換任何出現的getmax,後跟兩個引數,替換為替換表示式,並且還將每個引數替換為其識別符號,就像您期望它是一個函式一樣

// function macro
#include <iostream>
using namespace std;

#define getmax(a,b) ((a)>(b)?(a):(b))

int main()
{
  int x=5, y;
  y= getmax(x,2);
  cout << y << endl;
  cout << getmax(7,x) << endl;
  return 0;
}
5
7

已定義的宏不受塊結構的影響。宏一直有效,直到它被 #undef 預處理器指令取消定義

1
2
3
4
5
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
#undef TABLE_SIZE
#define TABLE_SIZE 200
int table2[TABLE_SIZE];

這將生成與以下程式碼相同的程式碼

1
2
int table1[100];
int table2[200];

函式宏定義接受替換序列中的兩個特殊運算子(#### 和 ##)
如果在替換序列中使用引數之前使用了運算子##,則該引數將被字串文字替換(就像它被雙引號括起來一樣)

1
2
#define str(x) #x
cout << str(test);

這將轉換為

1
cout << "test";

運算子#### 連線兩個引數,它們之間不留空格

1
2
#define glue(a,b) a ## b
glue(c,out) << "test";

這也會被翻譯成

1
cout << "test";

因為預處理器替換髮生在任何 C++ 語法檢查之前,所以宏定義可能是一個棘手的功能,但請小心:嚴重依賴複雜宏的程式碼對於其他程式設計師來說可能看起來很晦澀難懂,因為他們期望的語法在很多情況下與程式設計師在 C++ 中期望的正則表示式不同。

條件包含 (#ifdef, #ifndef, #if, #endif, #else 和 #elif)


這些指令允許在滿足特定條件時包含或丟棄程式的部分程式碼。

#ifdef允許僅在指定為引數的宏已定義時才編譯程式的一部分,無論其值如何。例如

1
2
3
#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif  

在這種情況下,程式碼行int table[TABLE_SIZE];只有在TABLE_SIZE先前已使用#define定義的情況下才會編譯,與其值無關。如果未定義,則該行將不會包含在程式編譯中。

#ifndef的作用正好相反:#ifndef#endif指令之間的程式碼僅在指定的識別符號之前未定義時才編譯。例如

1
2
3
4
#ifndef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];

在這種情況下,如果在到達這段程式碼時,TABLE_SIZE宏尚未定義,則會將其定義為值 100。如果它已經存在,它將保留其先前的值,因為#define指令將不會執行。

要放回的字元的#if, #else#elif(即“else if”)指令用於指定要滿足的條件,以便編譯它們所包圍的程式碼部分。緊隨其後的條件#if#if#elif或 #elif 只能評估常量表達式,包括宏表示式。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#if TABLE_SIZE>200
#undef TABLE_SIZE
#define TABLE_SIZE 200
 
#elif TABLE_SIZE<50
#undef TABLE_SIZE
#define TABLE_SIZE 50
 
#else
#undef TABLE_SIZE
#define TABLE_SIZE 100
#endif
 
int table[TABLE_SIZE];

請注意,#if, #elif#else鏈式指令的整個結構以#endif.

#endif#ifdef#ifndef結尾。#ifdef!defined分別可以在任何#if#if#elif#if 指令中使用以達到相同效果。

1
2
3
4
5
6
#if !defined TABLE_SIZE
#define TABLE_SIZE 100
#elif defined ARRAY_SIZE
#define TABLE_SIZE ARRAY_SIZE
int table[TABLE_SIZE];
#endif 

行控制 (#line)

當我們編譯程式並且在編譯過程中發生一些錯誤時,編譯器會顯示一條錯誤訊息,其中引用了發生錯誤的檔案的名稱和行號,因此更容易找到生成錯誤的程式碼。

要放回的字元的#line指令允許我們控制程式碼檔案中的行號以及發生錯誤時要顯示的檔名。它的格式是

#line number "filename"

其中數字是分配給下一行程式碼的新行號。從這一點開始,連續行的行號將逐一遞增。

"filename"是一個可選引數,允許重新定義將顯示的檔名。例如

1
2
#line 20 "assigning variable"
int a?;

此程式碼將生成一個錯誤,該錯誤將顯示為檔案"assigning variable"中的錯誤,第 20 行。

錯誤指令 (#error)

此指令在找到時中止編譯過程,生成一個編譯錯誤,該錯誤可以指定為其引數

1
2
3
#ifndef __cplusplus
#error A C++ compiler is required!
#endif 

如果宏名稱__cplusplus未定義,此示例將中止編譯過程(此宏名稱預設在所有 C++ 編譯器中定義)。

原始檔包含 (#include)

本教程的其他部分也經常使用此指令。當預處理器找到#include指令時,它會將其替換為指定檔案的全部內容。有兩種方法可以指定要包含的檔案

1
2
#include "file"
#include <file> 

兩種表示式之間的唯一區別是編譯器將查詢檔案的位置(目錄)。在第一種情況下,檔名在雙引號之間指定,首先在包含指令的檔案的同一目錄中搜索該檔案。如果不存在,編譯器將在配置為查詢標準標頭檔案的預設目錄中搜索該檔案。
如果檔名用尖括號括起來<>,則直接在配置為查詢標準標頭檔案的位置搜尋該檔案。因此,標準標頭檔案通常包含在尖括號中,而其他特定標頭檔案使用引號包含。

Pragma 指令 (#pragma)

此指令用於為編譯器指定各種選項。這些選項特定於您使用的平臺和編譯器。請參閱編譯器的手冊或參考,以獲取有關可以使用#pragma.

#pragma#pragma的特定引數,則會忽略它 - 不會生成錯誤。

預定義的宏名稱

以下宏名稱在任何時候都已定義

__LINE__表示正在編譯的原始碼檔案中的當前行的整數值。
__FILE__一個字串字面量,包含正在編譯的原始檔的假定名稱。
__DATE__格式為 "Mmm dd yyyy" 的字串字面量,包含編譯過程開始的日期。
__TIME__格式為 "hh:mm:ss" 的字串字面量,包含編譯過程開始的時間。
__cplusplus一個整數值。所有 C++ 編譯器都將此常量定義為某個值。如果編譯器完全符合 C++ 標準,則其值等於或大於 199711L,具體取決於它們符合的標準版本。

例如:

// standard macro names
#include <iostream>
using namespace std;

int main()
{
  cout << "This is the line number " << __LINE__;
  cout << " of file " << __FILE__ << ".\n";
  cout << "Its compilation began " << __DATE__;
  cout << " at " << __TIME__ << ".\n";
  cout << "The compiler gives a __cplusplus value of " << __cplusplus;
  return 0;
}
This is the line number 7 of file /home/jay/stdmacronames.cpp.
Its compilation began Nov  1 2005 at 10:12:29.
The compiler gives a __cplusplus value of 1
Index
目錄