後台開發 核心技術與應用實踐1 7 預處理

2021-09-23 16:13:02 字數 4135 閱讀 9538

1.7 預處理

c++提供的預處理功能主要有以下4種:巨集定義、檔案包含、條件編譯和布局控制。檔案包含在前面已描述過,下面重點描述巨集定義、條件編譯和布局控制,其中又著重講述常用巨集定義命令、do…while(0)的妙用、條件編譯及extern"c"塊的應用知識。

1.?常用巨集定義命令

#def?ine命令是乙個巨集定義命令,它用來將乙個識別符號定義為乙個字串,該識別符號被稱為巨集名,被定義的字串稱為替換文字。該命令有兩種格式:一種是簡單的巨集定義,另一種是帶引數的巨集定義。

簡單的巨集定義的宣告格式如下所示:

#define 巨集名 字串

例:#def?ine pi 3.1415926

帶引數的巨集定義的宣告格式如下所示:

#define 巨集 (參數列列) 巨集

例:#def?ine a(x) x

使用巨集定義中,要注意以下問題。

(1)在簡單巨集定義的使用中,當替換文字所表示的字串是乙個表示式時,需要加上括號,否則容易引起誤解和誤用。

【例1.17】 簡單巨集定義不加括號容易引起誤用。

#include

#define n 2+9

using namespace std;

int main()

程式的執行結果是:

29例1.17中就出現了問題:在此程式中存在著巨集定義命令,巨集n代表的字串是2+9,在程式中有對巨集n的使用,一般同學在讀該程式時,容易產生的問題是先求解n為2+9=11,然後在程式中計算a時使用乘法,即n*n=11*11=121,其實該題的結果為29,為什麼結果有這麼大的偏差?因為巨集展開是在預處理階段完成的,這個階段把替換文字只是看作乙個字串,並不會有任何的計算發生,在展開時是在巨集n出現的地方只是簡單地使用串2+9來代替n,並不會增添任何的符號,所以對該程式展開後的結果是a=2+9*2+9,計算後結果為29。要程式如之前想要的結果,只需要寫成#def?ine n (2+9),即加上括號就行。

(2)類似地,在帶引數的巨集定義的使用中,也容易引起誤解。例如當需要使用巨集替換來求任何數的平方,這時就需要使用引數,以便在程式中用實際引數來替換巨集定義中的引數。初學者容易寫成如例1.18中的形式。

【例1.18】 帶引數的巨集定義不加括號容易引起誤用。

#include

using namespace std;

#define area(x) x*x

int main()

程式的執行結果是:

8表面上看,給的引數是2+2,所得的結果應該為4*4=16,但該程式的實際結果為8。巨集定義中要遵循先替換後計算的原則,在上面的程式裡,2+2即為巨集area中的引數,應該由它來替換巨集定義中的x,即替換成2+2*2+2=8了。那如果遵循(1)中的解決辦法,把2+2括起來,即把巨集體中的x括起來,是否可以解決呢?#def?ine area(x) (x)*(x),對於area(2+2),替換為(2+2)*(2+2)=16,可以解決,但是對於area(2+2)/area(2+2)又會怎麼樣呢,有人一看到這道題馬上給出結果1,因為分子分母一樣,那麼這樣就又錯了。遵循先替換再計算的規則,這道題替換後會變為(2+2)*(2+2)/(2+2)*(2+2)即4*4/4*4按照乘除運算規則,結果為16/4*4=4*4=16。解決這類問題的方法是在整個巨集體上再加乙個括號,即#def?ine area(x) ((x)*(x)),不要覺得這沒必要,沒有它是不行的。

要想能夠真正使用好巨集定義,在讀別人的程式時,一定要記住先將程式中對巨集的使用全部替換成它所代表的字串,不要自作主張地新增任何其他符號,完全展開後再進行相應的計算,就不會求錯執行結果。

如果是自己在程式設計時使用巨集替換,則在使用簡單巨集定義時,當字串中不只乙個符號時,加上括號表現出優先順序,如果是帶引數的巨集定義,則要給巨集體中的每個引數加上括號,並在整個巨集體上再加乙個括號。

2.?do...while(0)的妙用

大家都知道,dowhile(condition)可以表示迴圈,但你有沒有遇到在一些巨集定義中可以不用迴圈的地方,也用到了dowhile,比如有這樣的巨集:

#define foo(x) dowhile(0)          // 這裡沒有分號

粗看會覺得很奇怪,既然迴圈裡面只執行了一次,那要這個看似多餘的do...while(0)有什麼意義呢?再來看這樣的巨集:

#define foo(x)

這兩個看似一樣的巨集,其實是不一樣的。前者定義的巨集是乙個非復合語句,而後者卻是乙個復合語句。假如有這樣的使用場景:

if(conditon)

foo(x);

else

...;

因為巨集在預處理的時候會直接被展開,採用第2種寫法,會變成:

if(condition)

statement one;

statement two;

else

...///

這樣會導致else語句孤立而出現編譯錯誤。加了dowhile(0),就使得巨集展開後,仍然保留初始的語義,從而保證程式的正確性。

3.?條件編譯

一般情況下,源程式中所有行的語句都參加編譯。但是有時程式設計師希望其中一部分內容只在滿足一定條件時才進行編譯,也就是對一部分內容指定編譯的條件,這就用到了「條件編譯」。

條件編譯命令最常見的形式為:

#ifdef 識別符號

程式段1

#else

程式段2

#endif

它的作用是:當識別符號已經被定義過(一般是用#def?ine命令定義),則對程式段1進行編譯,否則編譯程式段2。其中#else部分也可以沒有,即:

#ifdef 識別符號

程式段1

#endif

下面這樣的形式則是當指定的表示式值為真(非零)時就編譯程式段1,否則編譯程式段2。可以事先給定一定條件,使程式在不同的條件下執行不同的功能。

#if 表示式

程式段1

#else

程式段2

#endif

這裡的「程式段」可以是語句組,也可以是命令列。

有時候程式中的某些除錯**,只需要在除錯的時候被編譯,而不希望在程式的正式發行版中被編譯,你可能會看到類似例1.19這樣的**段。

【例1.19】 除錯**巧用條件編譯。

#include

using namespace std;

#define _debug_

int main()

程式的執行結果是:

file:test.cpp,line:7,x:10

當_debug_沒有被定義的時候,僅編譯#else與#endif之間的**;當定義了_debug_符號之後,則會編譯#ifdef與#else之間的**。要想定義乙個符號很簡單,只需要在檔案頭部加上像這樣的一條語句:

#define _debug_

用#def?ine命令的目的不在於用_debug_代表乙個字串,而只是表示已定義過_debug_,因此_debug_後面寫什麼字串都無所謂,甚至可以不寫字串。

4.?extern "c"塊的應用

經常能在c與c++混編的程式中看到這樣的語句:

#ifdef __cplusplus

extern "c"

#endif

其中,__cplusplus是c++的預定義巨集,表示當前開發環境是c++。在c++語言中,為了支援過載機制,在編譯生成的彙編**中,會對函式名字進行一些處理(通常稱為函式名字改編),如加入函式的引數型別或返回型別等,而在c語言中,只是簡單的函式名字而已,並不加入其他資訊,如下所示:

int func(int demo);

int func(double demo);

c語言無法區分上面兩個函式的不同,因為c編譯器產生的函式名都是_func,而c++編譯器產生的名字則可能是_func_fi和_func_fd,這樣就很好地把函式區別開了。

所以,在c/c++混合程式設計的環境下,extern "c"塊的作用就是告訴c++編譯器這段**要按c標準編譯,以盡可能地保持c++與c的相容性。例1.20說明了__cplusplus的使用方法。

【例1.20】 __cplusplus的使用方法。

#include

int main()

程式的執行結果是:

a c++ program

__cplusplus expands to "1"

例1.20中程式的意思是:如果沒有定義__cplusplus,那麼當前源**就會被當作c源**處理;如果定義了__cplusplus,那麼當前源**會被當中c++源**處理,並且輸出__cplusplus巨集被展開後的字串。

後台開發 核心技術與應用實踐

常用類庫 在c 中,臨時物件都是const型別的。可以使用union 聯合 判斷系統是大端 big endian 還是little endian 小端 只能把列舉賦值列舉變數,不能把元素的數值直接賦值列舉變數。共用體以最長的位元組為準,考慮記憶體對齊。結構體以內建型別的最小公倍數對齊。編譯階段 g ...

《後台開發核心技術與應用實踐》(一)

物件導向的c 後台開發核心技術與應用實踐 徐曉鑫 的學習筆記。後台開發技術能力體系,自省!後者先搜尋當前目錄,再搜尋標準頭檔案目錄 1 strlen 是函式,在執行時才能計算。引數必須是字元型指標 char 且必須是以 0 結尾的。當陣列名作為引數傳入時,實際上陣列已經退化為指標了。它的功能是返回字...

後台開發 核心技術與應用實踐1 2 函式

1.2 函式 1.函式的定義 乙個c程式是由若干個函式組成的,c語言被認為是面向函式的語言,而c 面向過程的程式設計也沿用了c語言使用函式的方法。在c 物件導向的程式設計中,主函式以外的函式大多是被封裝在類中的。主函式或其他函式可以通過類物件呼叫類中的函式。無論是c還是c 程式中的各項操作基本上都是...