【C語言】宏定義知識點小記

2020-08-13 20:36:59

一、一處修改,全域性生效

#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的值,在定義的宏中涉及多條語句時,需要使用」{}」,這樣子可以將多條語句作爲一個程式碼塊實現。

四、宏定義用」do{ }while(0)」

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

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

八、可變參數宏 … 和 _ VA_ARGS _

#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