現代c++中的預處理巨集
--徐東來
摘要:在c++從c繼承的遺產中,預處理巨集是其中的一部分。在現代c++的發展過程中,預處理巨集是否還有意義?本文將討論之。
關鍵字:
預處理 巨集#define #pragma
c++中有那麼多靈活的特性,例如過載、型別安全的模板、const關鍵字等等,為什麼程式設計師還要寫「#define」這樣的預處理指令?
典型的乙個例子,大家都知道「const int a=100;」就比「#define a 100」要好,因為const提供型別安全、避免了預處理的意外修改等。
然而,還是有一些理由讓我們去使用#define。
1)守護標頭檔案
為了防止標頭檔案被多次包含,這是一種常用技巧。
#ifndef myprog_x_h
#define myprog_x_h
// … 標頭檔案x.h的其餘部分
#endif
2)使用預處理特性
在除錯**中,插入行號或編譯時間這類資訊通常很有用,可以使用預定義的標準巨集,例如__file__、__line__、__date__和__time__。
3)
編譯時期選擇**
a.除錯**
選擇性的輸出一些除錯資訊:
void f()
通常我們也可以用條件判斷來代替:
void f()
// .. f()的其他部分
} b.
特定平台**
同一函式同一功能在不同的編譯平台上可能有不同的表現形式,我們可以通過定義巨集來區分不同的平台。
c.不同的資料表示方式
《深入淺出mfc>>這本書對mfc框架中巨集的使用解析的很透徹,也讓我們領略到巨集的強大功能。可以參看declare_message_map(),
begin_message_map,
end_message_map的實現。
4)#pragma的使用,例如用#pragma禁止掉無傷大雅的警告,用於可移植性的條件編譯中。例如,
包含winsock2 lib檔案:
#pragma comment(lib,
」ws2_32」)
用如下預處理巨集,可以使結構按1字結對齊:
#pragma pack(push)
#pragma pack(1)
// …
結構定義
#pragma pack(pop)
禁止掉某些警告資訊:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( error : 164 )// 把164號警告作為錯誤報出
// some code
#pragma warning( pop )
下面示範如何寫乙個簡單的預處理巨集max();這個巨集有兩個引數,比較並返回其中較大的乙個值。在寫這樣乙個巨集時,容易犯哪些錯誤?有四大易犯錯誤。
1)不要忘記為引數加上括號
// 例1:括號陷阱一:引數
//
#define max(a, b) a < b ? b : a
例如:max(i += 2, j)
展開後:
i += 2 < j ? j : i += 2
考慮運算子優先順序和語言規則,實際上是:
i += ((2 < j) ? j : i += 2)
這種錯誤可能需要長時間的除錯才可以發現。
2)不要忘記為整個展開式加上括號
// 例2:括號陷阱二:展開式
//
#define max(a, b) (a) < (b) ? (b) : (a)
例如:
m = max(j, k) + 42;
展開後為:
m = (j) < (k) ? (j) : (k) + 42;
考慮運算子優先順序和語言規則,實際上是:
m = ((j) < (k)) ? (j) : ((k) + 42);
如果j >= k, m被賦值k+42,正確;如果j < k, m被賦值j,是錯誤的。如果給展開式加上括號,就解決了這個問題。
3)當心多引數運算
// 例3:多引數運算
//
#define max(a, b) ((a) < (b) ? (b) : (a))
max(++j, k);
如果++j的結果大於k,j會遞增兩次,這可能不是程式設計師想要的:
((++j) < (k) ? (k) : (++j))
類似的:
max(f(), pi)
展開後:
((f()) < (pi) ? (pi) : (f()))
如果f()的結果大於等於pi,f()會執行兩次,這絕對缺乏效率,而且可能是錯誤的。
4)
名字衝突
巨集只是執行文字替換,而不管文字在哪兒,這意味著只要使用巨集,就要小心對這些巨集命名。具體來說,這個max巨集最大的問題是,極有可能會和標準的max()函式模板衝突:
// 例4:名字衝突
//
#define max(a,b) ((a) < (b) ? (b) : (a))
#include // 衝突!
在中,有如下:
templateconst t&
max(const t& a, const t& b);
巨集將它替換為如下,將無法編譯:
templateconst t&
((const t& a) < (const t& b) ? (const t& b) : (const t& a));
所以,我們盡量避免命名的衝突,想出乙個不平常的,難以拼寫的名字,這樣才能最大可能地避免與其他名字空間衝突。
巨集的其他缺陷:
5)巨集不能遞迴
容易理解。
6)
巨集沒有位址
你可能得到任何自由函式或成員函式的指標,但不可能得到乙個巨集的指標,因為巨集沒有位址。巨集之所以沒有位址,原因很顯然===巨集不是**,巨集不會以自身的形勢存在,因為它是一種被美化了的文字替換規則。
7)
巨集有礙除錯
在編譯器看到**之前,巨集就會修改相應的**,因而,他會嚴重改變變數名稱和其他名稱;此外,在除錯階段,無法跟蹤到巨集的內部。
C 預處理 巨集定義
開發乙個 c語言程式,讓它暫停 5 秒以後再輸出內容 helllo 並且要求跨平台,在 windows 和 linux 下 include 說明 在windows 作業系統和 linux作業系統下,生成原始碼不一樣 if win32 如果是windows平台,就執行 include elif lin...
C語言 巨集定義,預處理巨集
巨集是學習任何語言所不可缺少的,優秀的巨集定義可以使得 變得很簡潔且高效,有效地提高程式設計效率。巨集是一種預處理指令,它提供了一種機制,可以用來替換源 中的字串,直譯器或編譯器在遇到巨集時會自動進行這一模式替換 c語言有簡單的巨集系統,由編譯器或彙編器的預處理器實現。c的巨集預處理器的工作只是簡單...
c語言預處理 巨集定義
個人筆記 巨集定義對於用c語言程式設計的人是經常用,這裡只講使用中需注意的問題點和方便的用法。1.巨集擴充套件中空格對擴充套件結果的影響 define a y a expanded y a x 被擴充套件為 a expanded x define a y a expanded y a x 被擴充套件...