目录
宏定义的“Bug”
优先级
这是一个很简单的宏定义函数,用于计算n的平方:
#define sqr(n) n*n
看似没问题,但...
int a=sqr(2+3);
我们希望a的值是25,可他的值却是11!看看他展开后的样子就知道了:
int a=2+3*2+3;
乘法的优先级比加法高,所以先算3*2=6,再算2+6=8,最后算8+3=11。
那变成这样呢?
#define sqr(n) (n)*(n)
上面这个例子是没问题了,但如果换成这样:
int a=200/sqr(100);
我们又希望a的值为0(取整后的结果),但又事与愿违,他的值为200!
我们再看看他展开后的样子:
int a=200/(100)*(100);
这样原因就很明显了吧:200/100*100=200!
变量重复使用
你或许会说,那再改一下,改成这样:
#define sqr(n) ((n)*(n))
这样就安全多了(虽然代码变长了),但还有隐患,并且是光改宏函数改不了的(要改成普通函数或内联函数才行):
int a=5; //这个值你随便定
int b=sqr(++a);
现在我不想再给你搞什么逐步推导,搞循循善诱,该懂得你也懂了,现在我直接把他展开给你看:
int a=5; //这个值你随便定
int b=((++a)*(++a));
错误一目了然,不仅b的值不对,就连a的值也不对。所以只能把sqr改成这样:
template<typename T1,typename T2> inline T1 sqr(T2 n)
{
return n*n;
}
//2023.3.24修订:
template <typename Type> inline auto sqr(Type n) -> decltype(n*n)
{
return n*n;
}
你看,小小的一个宏定义函数,居然会被搞成这样!
2023.3.24修订:
其实宏定义函数也可以支持类似sqr(++a)这样的调用,但需要用到lambda函数的即时调用:
#define sqr(a) [](long double x) -> long double\
{\
return x*x;\
} (a)
虽然还是没有inline函数长,但是技术含量比较高。。。
关于内联函数,可以看看我的这篇博客:C++杂记(有感而发)
宏定义函数的好处
内存分配
但有时候宏定义函数还是有用的,比如分配内存:
#include <cstdlib>
#define malloc_mal(a,type) ((type*)malloc((a)*sizeof type)) //注意这里
#define malloc_new(type) (new (type)) //还有这里
#define mal_ar_new(s,type) (new (type)[(type)])
int main()
{
{
//use malloc_mal
int* p1;
char* p2;
double* p3;
p1=malloc_mal(10,int);
p2=malloc_mal(15,char);
p3=malloc_mal(105,double);
free(p1);
free(p2);
free(p3);
}
{
//use malloc_new
int* p1;
char* p2;
double* p3;
p1=malloc_new(int);
p2=malloc_new(char);
p3=malloc_new(double);
delete p1;
delete p2;
delete p3;
}
{
//use mal_ar_new
int* p1;
char* p2;
double* p3;
p1=mal_ar_new(10,int);
p2=mal_ar_new(15,char);
p3=mal_ar_new(105,double);
delete [] p1;
delete [] p2;
delete [] p3;
}
//do something......
}
这段代码分配了10个int型变量、15个char型变量、105个double型变量的内存空间,并把开头指针分别赋给p1,p2和p3。
这种功能是一般函数无法做到的(编译报错),所以宏定义函数也不是一无是处。
简短的可变参数
如果想要用fprintf实现一个m_printf(效果同printf,为防止函数名一样),可以这样:
template <typename... Types>
int m_printf(const char* format,Types args...)
{
return fprintf(stdout,format,args...);
}
但是,使用可变参数宏将会更短:
#define m_printf(format,...) fprintf(stdout,(format),__VA_ARGS__)
结果一样,可变参数宏更短。
结语
宏定义函数的限制放宽了很多,带来的影响有好有坏。
宏定义函数有时候可以让代码很短(使用不当的话会很长)。
一般(不是所有)的宏定义函数都可以用inline+模板函数实现(还更安全),但有一些还是做不到的。
但很多inline+模板函数都不可以用宏定义替换,所以能不用宏定义函数就不用吧!