本文主要介紹c語言中條件編譯相關的預編譯指令,包括#define、#undef、#ifdef、#ifndef、#if、#elif、#else、#endif、defined。
條件編譯是根據實際定義宏(某類條件)進行代碼靜態編譯的手段。可根據表達式的值或某個特定宏是否被定義來確定編譯條件。
最常見的條件編譯是防止重復包含頭文件的宏,形式跟下面代碼類似:
1 #ifndef ABCD_H 2 #define ABCD_H 3 4 // ... some declaration codes 5 6 #endif // #ifndef ABCD_H
在實現文件中通常有如下類似的定義:
1 #ifdef _DEBUG 2 3 // ... do some operations 4 5 #endif 6 7 #ifdef _WIN32 8 9 // ... use Win32 API 10 11 #endif
這些都是條件編譯的常用情境。
#define 定義一個預處理宏
#undef 取消宏的定義
#if 編譯預處理中的條件命令,相當於C語法中的if語句
#ifdef 判斷某個宏是否被定義,若已定義,執行隨後的語句
#ifndef 與#ifdef相反,判斷某個宏是否未被定義
#elif 若#if, #ifdef, #ifndef或前面的#elif條件不滿足,則執行#elif之後的語句,相當於C語法中的else-if
#else 與#if, #ifdef, #ifndef對應, 若這些條件不滿足,則執行#else之後的語句,相當於C語法中的else
#endif #if, #ifdef, #ifndef這些條件命令的結束標志.
defined 與#if, #elif配合使用,判斷某個宏是否被定義
#define命令定義一個宏:
#define MACRO_NAME[(args)] [tokens[(opt)]]
之後出現的MACRO_NAME將被替代為所定義的標記(tokens)。宏可帶參數,而後面的標記也是可選的。
宏定義,按照是否帶參數通常分為對象宏、函數宏兩種。
對象宏: 不帶參數的宏被稱為"對象宏(objectlike macro)"。對象宏多用於定義常量、通用標識。例如:
// 常量定義 #define MAX_LENGTH 100 // 通用標識,日志輸出宏 #define SLog printf // 預編譯宏 #define _DEBUG
函數宏:帶參數的宏。利用宏可以提高代碼的運行效率: 子程序的調用需要壓棧出棧, 這一過程如果過於頻繁會耗費掉大量的CPU運算資源。 所以一些代碼量小但運行頻繁的代碼如果采用帶參數宏來實現會提高代碼的運行效率。但多數c++程序不推薦使用函數宏,調試上有一定難度,可考慮使用c++的inline代替之。例如:
// 最小值函數 #define MIN(a,b) ((a)>(b)? (a):(b)) // 安全釋放內存函數 #define SAFE_DELETE(p) {if(NULL!=p){delete p; p = NULL;}}
#undef可以取消宏定義,與#define對應。
defined用來測試某個宏是否被定義。defined(name): 若宏被定義,則返回1,否則返回0。
它與#if、#elif、#else結合使用來判斷宏是否被定義,乍一看好像它顯得多余, 因為已經有了#ifdef和#ifndef。defined可用於在一條判斷語句中聲明多個判別條件;#ifdef和#ifndef則僅支持判斷一個宏是否定義。
#if defined(VAX) && defined(UNIX) && !defined(DEBUG)
和#if、#elif、#else不同,#ifdef、#ifndef、defined測試的宏可以是對象宏,也可以是函數宏。
條件編譯中相對常用的預編譯指令。模式如下:
#ifdef ABC // ... codes while definded ABC #elif (CODE_VERSION > 2) // ... codes while CODE_VERSION > 2 #else // ... remained cases #endif // #ifdef ABC
#ifdef用於判斷某個宏是否定義,和#ifndef功能正好相反,二者僅支持判斷單個宏是否已經定義,上面例子中二者可以互換。如果不需要多條件預編譯的話,上面例子中的#elif和#else均可以不寫。
#if可支持同時判斷多個宏的存在,與常量表達式配合使用。常用格式如下:
#if 常量表達式1 // ... some codes #elif 常量表達式2 // ... other codes #elif 常量表達式3 // ... ... #else // ... statement #endif
常量表達式可以是包含宏、算術運算、邏輯運算等等的合法C常量表達式,如果常量表達式為一個未定義的宏, 那麼它的值被視為0。
#if MACRO_NON_DEFINED // 等價於 #if 0
在判斷某個宏是否被定義時,應當避免使用#if,因為該宏的值可能就是被定義為0。而應當使用#ifdef或#ifndef。
注意: #if、#elif之後的宏只能是對象宏。如果宏未定義,或者該宏是函數宏,則編譯器可能會有對應宏未定義的警告。
本文主要介紹c語言中有關預編譯的指令。撰寫本文的目的在於理清相關概念調用,在後續預編譯使用時可以找到最合適的指令及格式。比如同時滿足多個宏定義的預編譯、多分支預編譯、#elif和#else指令的配合等。
參考資料:
Preprocessor Directives http://msdn.microsoft.com/zh-cn/library/aa381033
1.宏定義:用一個指定的標識符(即名字)來代表一個字符串,如:用PI代表3.1415926,#define PI 3.1415926
2.文件包含:指一個源文件可以將另外一個源文件的全部內容包含進來,#include<文件名>
3.條件編譯:對一部分內容指定編譯的條件,即滿足一定的條件才編譯,主要有:
(1)#ifdef標識符
程序段1
#eles
程序段2
#endif
(2)#ifndef標識符
程序段1
#eles
程序段2
#endif
(3))#if標識符
程序段1
#eles
程序段2
#endif
1 宏定義即是字符串替換。宏分為無參宏和含參宏。定義宏的位置可以在函數外部也可以在函數內部(vc++ 2008 測試通過)。宏的作用域是從定義處到取消定義命令[#undef 宏名]之間的部分,若無顯式的#undef命令則默認到文件結束。可以使用defined命令可以判斷宏是否被定義#if defined X (=#ifdef X),#if !defined X (= ifndef X)。 定義含參宏格式如#define SQ(y) ((y)*(y)),其中參數為y,宏得到的是y平方。為了保證宏替換的正確性,多加了3個括號。但這樣的保證也是有限的,它要求y的值不能在(y)內改變,如把y換成i++將得不到期望的結果。 宏調用(實際上是宏替換)不用考慮形參的類型,這帶來一定的好處。如求兩個數最大值的宏#define MAX(a,b) (a>b)?a:b,實參可以是int,double等。宏定義可以包括多個語句,如#define CHANGE(X1,X2,X3,X4) X1 += 1;X2 += 2; X3 += 3; X4 +=4; 2 文件包含命令#include的功能是把指定的文件插入該命令行位置取代該命令行,從而把指定的文件和當前的源程序文件連成一個源文件。 源代碼分布於多個文件時,建議使用調用文件+頭文件+實現文件的模式。頭文件中包含要用到的函數說明,類型定義,宏定義,常數值等。具體的實現放在實現文件中。在調用文件和實現文件中都包含該頭文件即可。為了避免重復包含頭文件,可在頭文件中使用#ifndef [頭文件標示符(如X_Header等等)] + 頭文件內容 + #endif模式。 3 條件編譯命令可以按不同的條件去編譯不同的程序部分,因而產生不同的目標代碼文件。 這對於程序的移植和調試很有用。條件編譯有三種形式,下面分別介紹:
第一種形式根據有無對應宏定義選擇編譯程序段:
#ifdef 標識符 // 或 #ifndef 標示符
程序段1
#else
程序段2
#endif 第二種形式根據常量表達式值選擇編譯,值為非0執行if段。#if 常量表達式
程序段1
#else
程序段2
#endif
這裡一定要是常量表達式,一般為宏。若是表達式包含變量則編譯器只能隨便猜一個了。 第三種形式含有#elif,看個例子吧#define ABC 3
void main(){#if ABC>0
int a=1;
printf("%d/n",a);
#elif ABC<0
int b=-1;
printf("%d/n",b);
#else
int c=0;
printf("%d/n",c);
#endif
} 其余的預編譯命令如下,這裡就不研究它們了。#line 標志該語句所在的行號
# 將宏參數替代為以參數值為內容的字符竄常量
## 將兩個相鄰的標記(token)連接為一個單獨的標記
#pragma 說明編譯器信息#warning 顯示編譯警告信息
#error 顯示編譯錯誤信息