#define PI ((double)3.1415926)
對於圓周率這種特殊的值,可以使用宏值替換,方便以後對於精度的修改等。
#define MAX_VAL(v1, v2) ((v1) > (v2) ? (v1) : (v2))
上面的宏用於獲取兩個值裏面的較大值,在定義的宏裏面,必要的括號是不可少的;雖然上面的宏加上了很多括號,但是在下面 下麪的語句中仍然會存在問題,雖然也有其他方式可以解決這樣子的問題,但是在編碼過程中還是不建議在宏裏面出現這樣子的操作,包括一些函數呼叫。
MAX_VAL(a++, b++)
#define SWAP_INT(v1, v2)\
{\
int tmp = 0;\
tmp = v1;\
v1 = v2;\
v2 = tmp;\
}
上面的宏的作用是交換兩個int的值,在定義的宏中涉及多條語句時,需要使用」{}」,這樣子可以將多條語句作爲一個程式碼塊實現。
if (v1 < v2)
SWAP_INT(v1, v2);
else
printf("swap_int : v1 = %d, v2 = %d.\n", v1, v2);
上面的語句使用SWAP_INT宏,編譯時報錯,因爲else沒有對應的if。可以修改成下面 下麪的形式,編譯時就不會發生錯誤了。
#define SWAP_INT(v1, v2)\
do{\
int tmp = 0;\
tmp = v1;\
v1 = v2;\
v2 = tmp;\
}while(0)
採用這樣子好處是可以避免分號帶來的麻煩,類似定義了一個程式碼塊,可以實現一個較複雜的功能,並且不會與上下文混淆。類似的宏經常在程式碼中出現來提升程式碼的健壯性。
在一個宏中的參數前面使用一個#,前處理器會把這個參數轉換爲一個字元陣列。
#define ERROR_LOG(module) fprintf(stderr, "error: "#module"\n")
#define PRINT_PARAM(param) printf("%s=%d\n",#param, param)
下面 下麪的語句執行的效果如下:
ERROR_LOG(max_val); -> error: max_val
ERROR_LOG("v1 = 0, v2 = 10"); -> error: "v1 = 0, v2 = 10"
PRINT_PARAM(v1); -> v1=10
PRINT_PARAM(v1 + v2); -> v1 + v2=10
「##」是一種分隔連線方式,它的作用是先分隔,然後進行強制連線。
在普通的宏定義中,前處理器一般把空格解釋成分段標誌,對於每一段和前面比較,相同的就被替換。但是這樣做的結果是,被替換段之間存在一些空格。如果我們不希望出現這些空格,就可以通過新增一些##來替代空格。
#define STR_CAT(src1, src2) (src1 src2) //GCC使用##連線字串編譯報錯替換成空格即可
#define CAT_PARAM(p1, p2) (p1##p2)
#define TYPE1(type,name) type name_##type##_type
#define TYPE2(type,name) type name##_##type##_type
下面 下麪的語句執行的效果如下:
int num = CAT_PARAM(12, 32); -> num = 1232
char *pDst = STR_CAT("acb", "def"); -> pDst = acbdef
TYPE1(int, c); -> int name_int_type ; (因爲##號將後面分爲 name_ 、type 、 _type三組,替換後強制連線)
TYPE2(int, d); -> int d_int_type ; (因爲##號將後面分爲 name、_、type 、_type四組,替換後強制連線)
typeof 需要標頭檔案<stddef.h>,它經常出現在宏定義中用來獲得變數的型別,如下:
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
這裏主要說明一下:
(void) (&_max1 == &_max2);
這一句很有意思:看起來是一句廢話,其實用得很巧妙!它主要是用來檢測宏的兩個參數 x 和 y 的數據型別是否相同。如果不相同,編譯器會給一個警告資訊,提醒程式開發人員。
warning:comparison of distinct pointer types lacks a cast
#define PRINT(...) printf(__VA_ARGS__)
#define LOG(format, args...) fprintf(stdout, format, args) // args...可以理解爲給可變參數...起別名,否則使用__VAR_ARGS__
LOG("%s","123"); //正確
LOG("123"); //錯誤 展開後,相當與LOG(,"123")
#define LOG(format, args...) fprintf(stdout, format, ##args)
LOG("%s","123"); //正確
LOG("123"); //正確 ##加上後不會進行連線。
##」連線符號的用法,「##」的作用是對token進行連線,上例中format,args都可以看作是token,如果token爲空,「##」則不進行連線,所以允許省略可變參數 因爲format這個token爲空,又因爲有##的加持,所以不連線。
##這個連線符號充當的作用就是當__VAR_ARGS__或者args爲空的時候,消除前面的那個逗號
需要注意的是凡宏定義裡有用’#‘或’##'的地方宏參數是不會再展開,例如:
#define STR(s) #s
printf("int max: %s\n", STR(INT_MAX)); // INT_MAX #include<climits>
這行會被展開爲:
printf("int max: %s\n", "INT_MAX");
https://www.cnblogs.com/alantu2018/p/8465911.html
https://zhuanlan.zhihu.com/p/55771861
https://www.cnblogs.com/hnrainll/archive/2012/08/15/2640558.html