关于C++宏定义函数的副作用

目录

目录

宏定义的“Bug”

优先级

变量重复使用

宏定义函数的好处

内存分配

简短的可变参数

结语

宏定义的“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+模板函数都不可以用宏定义替换,所以能不用宏定义函数就不用吧!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值