十三诫增修--09:慎用Macro(#define)

楼主: wtchen (没有存在感的人)   2016-05-23 14:59:55
诫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
作者: Caesar08 (Caesar)   2016-05-23 15:16:00
能不用macro就不要用macro...
楼主: wtchen (没有存在感的人)   2016-05-23 15:17:00
有时候macro还是很重要的,例如程式版本设定C语言是建议用MACRO设定不会变的常数(会比const快一点)
作者: Caesar08 (Caesar)   2016-05-23 15:23:00
C++03建议用const;C++11建议用constexpr话说还有enum
楼主: wtchen (没有存在感的人)   2016-05-23 15:27:00
对C来说const代表coder跟system保证不会修改该variable可是可以用indirect的方法去改,所以不算真的"常数"要设常数以C直观的作法还是#define @@constexpr好像只有编译期能用....
作者: LPH66 (-6.2598534e+18f)   2016-05-23 21:02:00
constexpr 的语意就是编译期常数所以它可以用在一些需要编译期常数的地方, 如阵列大小
作者: loveflames (咕啾咕啾魔法阵)   2016-05-24 23:26:00
variadic macro可以拿来实作条件展开写metaprogramming的时候用得着

Links booklink

Contact Us: admin [ a t ] ucptt.com