C語言中宏簡介

無參宏定義帶參宏定義固定參數宏可變參數宏多語句宏處理連接符條件判斷常見預定義宏

宏在C語言中是一段有名稱的代碼片段(使用#define定義),在預處理階段會把程序中的宏名替換爲對應的代碼片段,然後才進入編譯階段由編譯器進行編譯。

  • #define:宏定義
  • #undef:取消宏定義
  • #ifdef:判斷宏是否定義

無參宏定義

最簡單的宏定義時間用指定的標識符來代表代碼片段,當宏定義有多行時,需要在行尾使用\來連接。

#define 宏名稱  代碼片段

// 如(多行時,行尾使用\作爲續行符)
#define BUFFER_SIZE 1024
#define RANDOM (2.0*(double)rand() / RAND_MAX)
#define NUMBERS 1, \
                2, \
                3

int x[] = { NUMBERS };

帶參宏定義

類似函數定義,宏名稱後緊跟括號(名稱與括號間不能有空格)。

固定參數宏

“形參列表”是用逗號隔開的多個標識符(也可以空,表示無參數),實參列表中的實參數量必須與宏定義中的形參數量一樣多。

#define 宏名稱( [形參列表] )  替換文本
// 如
#define GETCHAR() getc(stdin)
// 
#define MAX(a,b) ((a)>(b)?(a):(b))

參數要用括號括起,避免實參爲表達式時出錯,如
#define MULTIPLY(a, b) ((a)*(b))
MULTIPLY(1+2, 3+4)會預處理爲((1+2)*(3+4)),若不加括號,則會變爲1+2*3+4(意義全變)。

即使加括號,宏在一定情況下還是會產生二義性(要避免此類使用),如
#define SQUARE(a) ((a)*(a))
SQUARE(a++)會預處理爲((a++)*(a++))(若a=3,則結果爲3*4)。

可變參數宏

C99標準允許定義有省略號的宏(在替換文本中使用\__VA_ARGS__標識可變參數),省略號必須放在參數列表的最後面表示可變參數。

#define OUTLOG(fmt, ...) printf(__func__, fmt, __VA_ARGS__)

多語句宏處理

對於有多條語句的複雜宏,爲避免錯誤,需要使用do{}while(0)包含(避免出現多餘分號等的報錯)。

#define SAFE_DELETE(ptr)  \
  do{ \
    if(NULL != ptr){  \
      delete ptr;     \
      ptr=NULL;       \
    } \
  }while(0)

連接符

在宏體中,通過在參數前面加不同符號,會有不同的擴展:

  • #:參數擴展爲字符串形式
  • ##:連接符,把兩部分連接爲一個標識符
  • #@:參數擴展爲字符

通過這些連接符,可以實現一些複雜的操作(如MFC中自動生成類等):

#define CHAR_VAR(n, c)  char var##n = #@c
#define ARRAY_VAR(n, str)  char ary##n[] = #str

// char vara='a';
CHAR_VAR(a, a);
// char arya[]="abc";
ARRAY_VAR(a, abc);

條件判斷

條件編譯裏面有判斷語句,如 #if 、#else 、#elif 及 #endif。類似if語句,在裏面可以使用與、或、非及比較表達式。通過條件判斷可以使代碼適應不同的運行環境(針對不同環境,編譯不同代碼部分)。

//#ifdef 是 '#if defined'的簡寫
#if defined(X_OS_WIN32) || defined(X_OS_WIN64) 
// Windows代碼處理...
#endif

VC中判斷當前是否爲Debug:

#ifdef _DEBUG
#pragma comment(lib, "my-debug.lib")
#else
#pragma comment(lib, "my-release.lib")
#endif

C++中定義標準C接口(使用extern "C")

#ifdef __cplusplus
extern "C"{
#endif

// ... 代碼

#ifdef __cplusplus
}
#endif

在判斷是否滿足指定要求

#if defined(MY_TEST_VER) && (MY_TEST_VER>=2)
// ...
#endif

常見預定義宏

預定義宏:

  • __DATE__: 字符串, 進行預處理的日期("Mmm dd yyyy", 如May 27 2006)
  • __TIME__: 字符串, 源文件的編譯時間("hh:mm:ss", 如09:11:10)
  • __FILE__: 字符串, 代表當前源代碼文件名(包含詳細路徑, 如F:/a.c)
  • __LINE__: 整數值, 代表當前源代碼文件中的行號
  • __STDC__: 布爾值, 爲1時表示該實現嚴格遵循ANSIC標準
  • __STDC_VERSION__: 長整型值, 表示編譯器所遵循的C標準的版本號(yyyymmL,如199101L)
  • __func__: 字符串, 當前所在函數名(C99標準)
  • __PRETTY_FUNCTION__: 在C中, 同__func__; 而在C++中, 則記錄了當前函數的頭信息
  • __VA_ARGS__: 保存了可變參數列表 "…"
  • __cplusplus: 長整型值, 表示了C++的版本號(yyyymmL, 如199711L)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章