[爆卦]C macro是什麼?優點缺點精華區懶人包

為什麼這篇C macro鄉民發文收入到精華區:因為在C macro這個討論話題中,有許多相關的文章在討論,這篇最有參考價值!作者wtchen (沒有存在感的人)看板C_and_CPP標題十三誡增修--09:慎用Macro(#...


誡9改了很多,有錯請告知....

========================================================

09. 慎用macro(#define)

Macro是個像鐵鎚一樣好用又危險的工具:
用得好可以釘釘子,用不好可以把釘子打彎、敲到你手指或被抓去吃子彈。
因為macro 定義出的「偽函式」有以下缺點:

(1) debug會變得複雜。
(2) 無法遞迴呼叫。
(3) 無法用 & 加在 macro name 之前,取得函式位址。
(4) 沒有namespace。
(5) 可能會導致奇怪的side effect或其他無法預測的問題。

所以,使用macro前,請先確認以上的缺點是否會影響你的程式運行。

替代方案:enum(定義整數),const T(定義常數),inline function(定義函式)
C++的template(定義可用不同type參數的函式),
或C++11開始的匿名函式(Lambda function)與constexpr T(編譯期常數)


以下就針對macro的缺點做說明:


(1) debug會變得複雜。

編譯器不能對macro本身做語法檢查,只能檢查預處理(preprocess)後的結果。


(2) 無法遞迴呼叫。

根據C standard 6.10.3.4,
如果某macro的定義裡裏面含有跟此macro名稱同樣的的字串,
該字串將不會被預處理。

所以:

#define pr(n) ((n==1)? 1 : pr(n-1))
cout<< pr(5) <<endl;

預處理過後會變成:

cout<< ((5==1)? 1 : pr(5 -1)) <<endl; // pr沒有定義,編譯會出錯


(3) 無法用 & 加在 macro name 之前,取得函式位址。

因為他不是函式,所以你也不可以把函式指標套用在macro上。


(4) 沒有namespace。

錯誤例子:

#define begin() x = 0

for (std::vector<int>::iterator it = myvector.begin();
it != myvector.end(); ++it) // begin是std的保留字
std::cout << ' ' << *it;

改善方法:macro名稱一律用大寫,如BEGIN()


(5) 可能會導致奇怪的side effect或其他無法預測的問題。

錯誤例子:

#include <stdio.h>
#define SQUARE(x) (x * x)
int main()
{
printf("%d\n", SQUARE(10-5)); // 預處理後變成SQUARE(10-5*10-5)
return 0;
}

正確例子:在 Macro 定義中, 務必為它的參數個別加上括號

#include <stdio.h>
#define SQUARE(x) ((x) * (x))
int main()
{
printf("%d\n", SQUARE(10-5));
return 0;
}

不過遇到以下有side effect的例子就算加了括號也沒用。

錯誤例子: (感謝 yaca 網友提供)
#define MACRO(x) (((x) * (x)) - ((x) * (x)))
int main()
{
int x = 3;
printf("%d\n", MACRO(++x)); // 有side effect
return 0;
}

備註:

C++11開始支援匿名函式(Lambda function),可視情況使用。

補充資料:

- http://stackoverflow.com/questions/14041453/why-are-preprocessor-
macros-evil-and-what-are-the-alternatives

- http://stackoverflow.com/questions/12447557/can-we-have-recursive-macros

- C11 Standard 6.10.3.4

- http://en.cppreference.com/w/cpp/language/lambda

--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.128.143.228
※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1463986797.A.CC6.html
Caesar08: 能不用macro就不要用macro... 05/23 15:16
wtchen: 有時候macro還是很重要的,例如程式版本設定 05/23 15:17
wtchen: C語言是建議用MACRO設定不會變的常數(會比const快一點) 05/23 15:18
Caesar08: C++03建議用const;C++11建議用constexpr 05/23 15:23
Caesar08: 話說還有enum 05/23 15:23
※ 編輯: wtchen (220.128.143.228), 05/23/2016 15:26:06
wtchen: 對C來說const代表coder跟system保證不會修改該variable 05/23 15:27
wtchen: 可是可以用indirect的方法去改,所以不算真的"常數" 05/23 15:27
wtchen: 要設常數以C直觀的作法還是#define @@ 05/23 15:28
wtchen: constexpr好像只有編譯期能用.... 05/23 15:29
※ 編輯: wtchen (223.136.189.53), 05/23/2016 20:44:41
LPH66: constexpr 的語意就是編譯期常數 05/23 21:02
LPH66: 所以它可以用在一些需要編譯期常數的地方, 如陣列大小 05/23 21:04
loveflames: variadic macro可以拿來實作條件展開 05/24 23:26
loveflames: 寫metaprogramming的時候用得著 05/24 23:28

你可能也想看看

搜尋相關網站