Learning C++ 之 1.10 初識預處理器

預處理器是一個很好的思想,即一個在程序編譯之前提前獨立運行的一段代碼。當預處理器運行的時候,他會從code的頭到尾尋找特殊指令。特殊指令一般指的是以#開頭的,回車結束的代碼(不是;號結尾)。有非常多的特殊指令符,下面會一一介紹。

預處理器並不是智能的,他不理解C++語法,她只是在編譯器運行之前操作腳本,然後預處理器的結果輸出給編譯器。預處理器並不會修改代碼,相反,預處理器做的所有操作都是存在臨時內存裏的。

include

之前你已經見過#include的預處理指令了,他就是將文件copy到引用的地方過來。當你需要在很多地方引用同一個文件的時候,這個預處理命令是非常有效的。

#inlcude雲處理命令有兩個格式:

#inckude <>

該格式表示include的是標準函數庫裏的文件,如C++標準庫。

#include ""

該格式表示引用的是你自己寫的頭文件,一般情況下會檢查當前文件,如果沒有就去其他路經查找。

宏定義:

預處理器可以定義一個宏,宏定義了一個特定的輸入序列(標識符)轉化成特定的輸出序列(文本文字)的規則。

有兩種基本類型的宏:函數替換和對象替換

函數替換宏的功能類似於函數,也有類似的目的。我們不做討論,而且他們的使用通常是危險的,而且都可以被內聯函數替換。

對象替換宏有兩種定義方式:

#define identifier
#define identifier substitution_text

上一行的聲明並沒有替換成文本,但是下一行替換了。因爲這是宏聲明,所以沒有;號。

有替換文本的對象替換宏

當預處理器碰到該指令會把所有的對象替換成相應的文本,一般文本都是使用大寫字母,下劃線隔開。

看一下下面的例子:

#define MY_FAVORITE_NUMBER 9
 
std::cout << "My favorite number is: " << MY_FAVORITE_NUMBER << std::endl;

預處理器會將該行轉換成:

std::cout << "My favorite number is: " << 9 << std::endl;

當執行的時候就會輸出:My favorite number is 9

我們在2.9部分會詳細討論該種用法。

沒有替換文本的對象替換宏:

如下面的例子:

#define USE_YEN

像這種格式的宏,你可能會想:一種將來出現的情況,標識符被刪除了或者被任何其他的標識符替代了。

這個可能非常沒有意義,因爲宏是用來做文本替換的。然而這並不是這種格式的使用的地方,這個我們馬上就會介紹。

與替換文本的宏一樣,該種格式的宏通常也是被認可的。

條件編譯:

條件編譯預處理器指令允許你在特定情況下編譯或者不編譯一段代碼。我們一般碰到的條件編譯有#ifdef  #ifndef   #endif

#ifdef 表示讓處理器檢查後面定義的值是飯否被#define定義過,如果定義過,那麼就會編譯#ifdef  到 #endif之間的代碼。如果沒有定義,就不編譯該部分。

參考下面的例子:

#define PRINT_JOE
 
#ifdef PRINT_JOE
std::cout << "Joe" << std::endl;
#endif
 
#ifdef PRINT_BOB
std::cout << "Bob" << std::endl;
#endif

上面的例子會輸出:Joe。因爲define了PRINT_JOE,所以走第一個分支。同樣如果是下面的代碼:

#define PRINT_JOE
 
#ifndef PRINT_JOE
std::cout << "Joe" << std::endl;
#endif
 
#ifndef PRINT_BOB
std::cout << "Bob" << std::endl;
#endif

該例子就會輸出Bob,因爲PRINT_BOB沒有定義。

條件編譯經常用到的地方是頭文件警衛的作用,這個在下一節會詳細介紹。

你可能會有疑惑:

#define PRINT_JOE
 
#ifdef PRINT_JOE
// ...

既然我們定義PRINT_JOE什麼都沒有,爲什麼在#ifdef中沒有把PRINT_JOE替換成什麼都沒有呢?宏只會對普通代碼起作用,其他的預處理器命令不做處理。因此#ifdef中的PRINT_JOE是獨立的。

例如:

#define FOO 9 // Here's a macro substitution
 
#ifdef FOO // This FOO does not get replaced because it’s part of another preprocessor directive
    std::cout << FOO; // This FOO gets replaced with 9 because it's part of the normal code
#endif

定義的範圍:

指令在編譯前得到解析,從文件的頭到尾。一旦預處理器完成之後,所有的預處理指令就廢棄了。

這意味着指令在定義到文件結束是有效的。在同一個代碼中的指令不會對同項目中的其他文件產生影響。

考慮下面的例子:

function.cpp

#include <iostream>
 
void doSomething()
{
#ifdef PRINT
    std::cout << "Printing!";
#endif
#ifndef PRINT
    std::cout << "Not printing!";
#endif
}

main.cpp

void doSomething(); // forward declaration for function doSomething()
 
int main()
{
#define PRINT
 
    doSomething();
 
    return 0;
}

上面的例子會打印:Not printing! 因爲main中定義的PRINT對function中是無效的。

最後,在一個頭文件中的指令的定義可以被#include進很多文件中。這提供了一個方法,可以在一個地方提供指令,同時在多個文件中使用。下一節我們會看到很多例子。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章