宏定义(#define)的作用及使用方法属于C语言基础的范畴,本文不打算多做赘述,在此只是给出#define可能引起的或潜在的风险,希望对你有所帮助。
首先,就是常见的#define和typedef的区别,这个C语言的书都会解释,不过既然说起#define了那就顺便再写一次吧,看如下定义
int_ptr p_1st, p_2nd;
/**
* 程序编译经扩展后变成
*/
int * p_1st, p_2nd;
/**
* 可见p_1st是指针,而p_2nd就是一整形变量
*/
typdedef int * int_ptr
int_ptr p_1st, p_2nd;
/**
* 现在p_1st和p_2nd都是int类型的指针
*/
下面才是编程中#define可能会引起的黑洞,先看这个宏定义
这个宏做一个加法的运算,你觉得它有什么问题吗??看看如下的调用会发生什么情况
这里就有情况了,由于运算符的优先顺序不同,那么这里并不能得到我们期望的值。好的,我们可以这样解决
好,上面的问题现在解决了, 再看看下面的代码
int j = 2;
int a = MAX(i++, j++);
现在新的问题又出现了,这段代码还是不能得到我们期望的结果,可能你会说这么用的人有病吗,干吗传这么个参数,但是实际项目中的代码各式各样,出现一次就是麻烦是不是。由于这个问题,因此在C++里建议用inline函数代替宏定义,但是无论如何在C语言的项目里宏定义是非常广泛的存在的,了解一些宏的负面作用还是有助于你写出预防性的代码。
下面我们再看看#define的另外一个可能的缺陷,先看这个代码
2 DO_A(); \
3 DO_B();
直接使用这个宏会有问题吗,假设我们如下使用它
2 TEST_MACRO();
它就会扩展成这样
2 DO_A();
3 DO_B();
这样DO_B()原本是有条件的执行,现在是任何时候都无条件执行,视编译器而定,有的编译器能解决这种问题,但是单从C语言语法本身的角度来讲这样就有问题,依赖于编译器来解决问题是有风险的。也许你想用括号{}来解决问题,变成这样
DO_A(); \
DO_B(); \
}
好,上面的问题解决了,但是如果这样定义宏并且如下使用呢:
if (A) \
DO_A(); \
else \
DO_B(); \
}
如下使用
if (...)
TEST_MACRO();
else
{
...
}
宏展开后:
if (...) { \
if (A) \
DO_A(); \
else \
DO_B(); \
};
else
{
...
}
扩展后第二个 else 前边多了一个分号,编译都不能通过,这其实是好的情况,因为你可以把问题扼杀在编译阶段,如果编译通过但是程序跑出奇怪的行为那才是痛苦。因此关于宏定义就有了do{} while(0)的使用方法
2 if (true) \
3 DO_A(); \
4 else \
5 DO_B(); \
6 }while(0)
使用do{} while(0) 方法就是为了类似的宏可以在任何时候使用,总之宏定义正反作用都有并且在C++中是不推荐使用的(C++中建议用const定义常量、用inline定义函数),但是在C语言的项目中宏是大量存在的,不过程序终究是人写的,还是看人怎么去用它