一、前言
在后台程序運行出問題時,需要查看詳盡的日志,C語言提供記錄日志觸發點文件名、行號、函數名的方法,關鍵是利用C99新增的預處理標識符__VA_ARGS__;先介紹幾個編譯器內置的宏定義,這些宏定義不僅可以幫助我們完成跨平台的源碼編寫,靈活使用也可以巧妙地幫我們輸出非常有用的調試信息。
二、ANSI C標准宏
__LINE__ // 在源代碼中插入當前源代碼行號 __FILE__ // 在源文件中插入當前源文件名 __DATE__ // 在源文件中插入當前的編譯日期 __TIME__ // 在源文件中插入當前編譯時間 __STDC__ // 當要求程序嚴格遵循ANSI C標准時該標識被賦值為1 __cplusplus // 當編寫C++程序時該標識符被定義 _WIN32 // 在程序運行在windows系統上被定義位1 linux // 在程序運行在linux系統上被定義位1 __x86_64__ // 在程序運行在64位系統上被定義位1 __i386__ // 在程序運行在32位系統上被定義位1 __VA_ARGS__ // 是一個可變參數的宏,這個可宏是新的C99規范中新增的, // 目前似乎gcc和VC6.0之后的都支持(VC6.0的編譯器不支持)。 // 宏前面加上##的作用在於,可以接受參數為0個或者多個
三、實例
宏實例:
#include <stdio.h> int main() { printf("__func__:%s\n", __func__); printf("__FILE__:%s\n", __FILE__); printf("__DATE__:%s\n", __DATE__); printf("__TIME__:%s\n", __TIME__); printf("__LINE__:%d\n", __LINE__); return 0; }
宏實例程序輸出如下:
__func__:main __FILE__:main.c __DATE__:Sep 14 2019 __TIME__:14:26:36 __LINE__:9
四、#和##運算符
其中#和##運算符的功能有所不同,在這里也做一定的介紹
1. #用來把參數轉換成字符串
實例1:
#define P(A) printf("%s:%d\n", #A, A); int main() { int a = 1, b = 2; P(a); P(b); P(a+b); return 0; }
實例1程序輸出如下:
a:1 b:2 a+b:3
實例2:
#define SQUARE(x) printf("The square of "#x" is %d.\n", ((x)*(x))) int main() { SQUARE(8); return 0; }
實例2程序輸出如下:
The square of 8 is 64
2. ##運算符可以用於宏函數的替換部分
##就是個粘合劑,將前后兩部分粘合起來,也就是有“組成變量名”的意思。特別要和#運算符的功能區分開來,#是連接字符串,而##是連接變量名。
但是“##”不能隨意粘合任意字符,必須是合法的C語言標示符。在單一的宏定義中,最多可以出現一次“#”或“##”預處理操作符。如果沒有指定與“#”或“##”預處理操作符相關的計算次序,則會產生問題。為避免該問題,在單一的宏定義中只能使用其中一種操作符(即,一份“#”或一個“##”,或都不用)。除非非常有必要,否則盡量不要使用“#”和“##”。
實例程序:
#include <stdio.h> #define XNAME(n) SYSTEM_ ## n int main() { int SYSTEM_ = 0, SYSTEM_OPEN = 1, SYSTEM_WRITE = 2, SYSTEM_CLOSE = 3; printf("%d\n", XNAME()); printf("%d\n", XNAME(OPEN)); printf("%d\n", XNAME(WRITE)); printf("%d\n", XNAME(CLOSE)); return 0; }
實例程序輸出:
0 1 2 3
此外,__VA_ARGS__ 是一個可變參數的宏,很少人知道這個宏,這個可變參數的宏是新的C99規范中新增的,目前似乎只有gcc支持(VC6.0的編譯器不支持)。實現思想就是宏定義中參數列表的最后一個參數為省略號(也就是三個點)。
##__VA_ARGS__ 宏前面加上##的作用在於,當可變參數的個數為0時,這里的##起到把前面多余的逗號去掉的作用,否則會編譯出錯,相當於能夠接受0個及以上的參數。
##__VA_ARGS__的實例:
#define my_print1(fmt,...) printf(fmt, __VA_ARGS__) my_print1("iiijjj\n") // 錯誤打印 my_print1("i=%d,j=%d\n",i,j) // 正確打印 #define my_print2(fmt,...) printf(fmt, ##__VA_ARGS__) my_print2("iiijjj\n") // 正確打印 my_print2("i=%d,j=%d\n",i,j) // 正確打印