建議:
規則:巨集是危險的,用法與真正的函式相似,但是具有不同的語義。c99在c中增加了內聯函式,當內聯函式和巨集可以互換使用時,應該優先選擇內聯函式,內聯替換並不是文字替換,也沒有建立函式,決定乙個函式是否為內聯函式是乙個底層的優化細節,編譯器應該不依賴程式換做出這個決定,是否使用內聯函式取決於目標編譯器對它們的支援,它們對系統效能特徵所產生的影響以及可移植性問題,靜態函式常常具有與內聯函式相同的優點。
下面的例子中,當傳遞給cube巨集的引數是乙個具有***的表示式時,這個巨集就具有未定義的行為。
**1:
#define cube(x) ((x) * (x) * (x))在這個例子中,a的初始化表示式展開為: int a = 81/((++i) * (++i) * (++i));/*...
*/int i = 2
;int a = 81 / cube(++i);
解決方案:
inline int cube(int**2:x)/*
...*/
int i = 2
;int a = 81 / cube(++i);
#includesize_t count = 0執行結果:;#define exec_bump(func) (func(), ++count)
void g(void
) void afunc(void) }
int main(void
)
解決方案:
#includesize_t count = 0執行結果:;void g(void
) typedef
void(*exec_func)(void
);inline
void
exec_bump(exec_func f)
void afunc(void) }
int main(void
)
和函式不同,巨集的執行可以是交錯的,兩個巨集單獨執行時無害,但是它們在同乙個表示式中組合在一起時可能導致未定義的行為:
**3:
#define f(x) (++operations, ++calls_to_f, 2 * x)operations變數在同乙個表示式中讀取並修改了2次,因此按照某種順序,可能會接收到錯誤的值#define g(x) (++operations, ++calls_to_g, x + 1)
/*...
*/y = f(x) + g(x);
解決方案:
inline int f(int**1:x) inline
int g(int
x) /*
...*/
y = f(x) + g(x);
#define cube(i) (i * i * i)被展開為: int a = 81 / (2 + 1 * 2 + 1 * 2 + 1);int a = 81 / cube(2 + 1)
解決方案:
#define cube(i) ((i) * (i) * (i))例外:當替換文字中的引數名由逗號分隔時,不管實際引數如何複雜,不需要對巨集引數加上括號,因為逗號操作符的優先順序低於其他任何操作符int a = 81 / cube(2 + 1)
#define foo(a, b, c) bar(a, b, c)巨集替換列表應該加上括號,以保護表示式中所有優先順序較低的操作符/*...
*/foo(arg1, arg2, arg3);
**1:
#define cube(x) (x) * (x) * (x)解決方案:int i = 3
;int a = 81 /cube(i);
//被展開為: int a = 81 / i * i * i
#define cube(x) ((x) * (x) * (x))這個方案最好實現為內聯函式int i = 3
;int a = 81 / cube(i);
如果需要對型別進行編碼,應該使用型別定義(typedef)而不是巨集定義(#define)。型別定義遵循作用域規則,而巨集定義卻不遵循
**1:
#define cstring char *cstring s1, s2;其中s1宣告為char *,s2宣告為char
解決方案:
typedef char *cstring;如果乙個檔案與標準標頭檔案同名。並且位於包含原始檔的搜尋路徑中,其行為是未定義的cstring s1, s2;
建議:不要復用標準頭檔名、系統特定的頭檔名或其他的頭檔名
防止標頭檔案沒多次包含或是忘記包含,通過一種簡單的技巧:每個標頭檔案應該用#define指令定義乙個符號,表示已經被包含,然後整個標頭檔案出現在乙個包含防護條件中:
#ifndef header_h兩個連續的問號表示乙個三字串行,據c99標準,在乙個原始檔中,下列這些3個字元的連續出現被對應的單個字元所替換#define header_h
/*....header的內容
*/#endif
??=#
??)]
??!|
??([
??'^
??>
}??/
\??<
{??-~
**1:
//由於??/等價於\,a++相當於被注釋掉what is the value of a now ??/
a++;
解決方案:
//**1:what is the value of a now? ?/
a++;
#include#include 「library.h」library.h和library.h可能表示同乙個檔案,並不清楚utilities_math和utilities_physics能否進行區分#include
"library.h
"#include
"utilities_math.h
"#include
"utilities_physics.h
"#include
"my_library.h
"
解決方案:
#include#include 「lib_main.h」巨集經常用於修補現有的**,用乙個識別符號對另乙個識別符號進行全域性替換,但是這種做法存在一些風險,當乙個函式被乙個不夠安全的函式替換時,這種做法就顯得特別的危險#include
"lib_2.h
"#include
"util_math.h
"#include
"util_physics.h
"#include
"my_library.h
"
**:
#define vsnprintf(buf, size, fmt, list) \vsprintf(buf, fmt, list)vsprintf函式並不會檢查邊界,因此size引數將被丟棄,在使用不信任的資料的時候可能會導致潛在的緩衝區溢位問題
解決方案:
#include#ifndef __use_isoc99參見《c語言中do...while(0)用法小結/*重新實現 vsnprintf()
*/#include
"my_stdio.h
"#endif
》《c安全編碼標準》
wuyudong
出處:
c 預處理和預處理命令
預處理發生在編譯之前,預處理輸出的是乙個單一的檔案,這個檔案被送到編譯器,進行編譯。每條預處理命令都控制預處理器的行為。每條預處理命令佔據一行,有以下的格式 character 預處理命令 one of define,undef,include,if,ifdef,ifndef,else,elif,e...
C 預處理命令
c 提供的預處理功能 巨集定義 檔案包含和條件編譯 分別由巨集定義命令 檔案包含命令和條件編譯命令三種預處理命令來實現。預處理命令 格 式 預處理命令 末尾不加分號 作用域 從定義點到程式結束 說 明 預處理命令獨佔一行,位置任意 巨集定義命令 格 式 define 巨集名 形參 巨集體 undef...
C 預處理指令
1.define 通常和 if一起使用 使用 define可以定義乙個符號,並通過將該符號用作表示式傳遞給 if 指令,使該表示式的計算結果為true 比如 preprocessor if.cs define debug define vc v7 using system public class ...