ideasman42..
12
如有疑问,请使用函数(或内联函数).
然而,这里的答案主要解释宏的问题,而不是有一些简单的观点,宏是邪恶的,因为愚蠢的事故是可能的.
你可以意识到陷阱并学会避免它们.然后只有在有充分理由的情况下才使用宏.
在某些特殊情况下,使用宏有一些优势,包括:
通用函数,如下所述,您可以拥有一个可用于不同类型的输入参数的宏.
它们可以任选包括本地信息,如调试字符串:
(__FILE__,__LINE__,__func__).检查前/后条件,assert失败,甚至静态断言,因此代码不会在不正确的使用时编译(主要用于调试版本).
检查输入参数,你可以对输入参数进行测试,例如检查它们的类型,sizeof,struct在转换之前检查成员是否存在
(对多态类型有用).
或检查阵列是否符合某种长度条件.
请参阅:https://stackoverflow.com/a/29926435/432509
虽然它注意到函数进行类型检查,但C也会强制转换值(例如ints/floats).在极少数情况下,这可能会有问题.它可以编写宏,这些宏比关于输入args的函数更严格.请参阅:https://stackoverflow.com/a/25988779/432509
它们用作函数的包装器,在某些情况下你可能想避免重复自己,例如...... func(FOO, "FOO");,你可以定义一个宏来为你扩展字符串func_wrapper(FOO);
当你想在调用者本地范围内操作变量时,将指针传递给指针的工作正常,但在某些情况下,使用宏的麻烦仍然较少.
(对于每个像素的操作,多个变量的赋值是一个例子,你可能更喜欢宏而不是函数......虽然它仍然在很大程度上依赖于上下文,因为inline函数可能是一个选项).
不可否认,其中一些依赖于非标准C的编译器扩展.这意味着您最终可能会使用较少的可移植代码,或者必须使用ifdef它们,因此只有在编译器支持时才会利用它们.
避免多个参数实例化
注意到这是因为它是宏中最常见的错误原因之一(x++例如,传入宏可能会增加多次).
它可以编写宏来避免副参数的多重实例化.
如果你想拥有square适用于各种类型并且支持C11的宏,你可以这样做......
inline float _square_fl(float a) { return a * a; }
inline double _square_dbl(float a) { return a * a; }
inline int _square_i(int a) { return a * a; }
inline unsigned int _square_ui(unsigned int a) { return a * a; }
inline short _square_s(short a) { return a * a; }
inline unsigned short _square_us(unsigned short a) { return a * a; }
/* ... long, char ... etc */
#define square(a) \
_Generic((a), \
float: _square_fl(a), \
double: _square_dbl(a), \
int: _square_i(a), \
unsigned int: _square_ui(a), \
short: _square_s(a), \
unsigned short: _square_us(a))
这是GCC,Clang,EKOPath和Intel C++支持的编译器扩展(但不支持MSVC) ;
#define square(a_) __extension__ ({ \
typeof(a_) a = (a_); \
(a * a); })
因此,宏的缺点是你需要知道使用它们开始,并且它们不被广泛支持.
一个好处是,在这种情况下,您可以square为许多不同类型使用相同的功能.