定义宏时千万不要把宏的“参数名”和宏中用到的结构体成员名相同!

  C语言中的宏非常强大,可以利用宏来一些要经常用到的操作封装在一起,或者有时仅仅只是让代码看起来更整洁些,但是宏是没有类型检查的,所以使用时千万要小心。但是有时也可以利用宏的没有类型检查这个特性,实现一些特殊的需求。举个简单的例子来说,下面的两个函数:
int conf_add_fd(conf_snd_t *conf_snd, int fd) 
{
    fd_snd_t  *fd_snd = malloc(sizeof (fd_snd_t));
    if (fd_snd) {
        return list_add(&conf_snd->fd_list, fd_snd->list);
    }
    return -1;
}

int hf_add_fd(hf_snd_t *hf_snd, int fd) 
{
    fd_snd_t  *fd_snd = malloc(sizeof (fd_snd_t));
    if (fd_snd) {
        return list_add(&hf_snd->fd_list, fd_snd->list);
    }
    return -1;
}

上面的两个函数相似度非常高,如果用两个函数来做这个事情,因为有类型检查的约束,所有会有两个几乎完全相同的函数。当然可能也有人说,可以讲第一个函数定义为void *,再加一个区分的参数,这种方式除了函数调用产生的开销(当然这个也可以通过inline来避免),还有判断的开销。这是宏就是一个很好的选择,可以让这两个函数都调用下面的宏:

#define __snd_add_fd(owner, fd) ({
    fd_snd_t  *fd_snd = malloc(sizeof (fd_snd_t));
    int        ret = -1;
    if (fd_snd) {
        ret = list_add(&owner->fd_list, fd_snd->list);
    }
    ret;
})

利用这个宏,两个函数就会成为下面的样子:

int conf_add_fd(conf_snd_t *conf_snd, int fd) 
{
    return __snd_add_fd(conf_snd, fd);
}

int hf_add_fd(hf_snd_t *hf_snd, int fd) 
{
    return __snd_add_fd(hf_snd, fd);
}

代码是不是比之前简洁很多,而且宏是在预处理时会替换掉的,也就是代码是直接放在函数里的,避免了函数调用的开销。我这里使用了gcc的一个扩展“({ 代码 })”,里面可以有代码的逻辑处理,而且可以有返回值。

  宏绝对是C语言中非常强大的工具,而且也是非常好用的。但是凡事都有个度,如果过度地使用宏,只会让你的代码更加难以理解。宏还有一个弊端,就是调试起来麻烦。在使用宏时,还有一个地方要特别注意。宏在预处理过程中,只是将宏中的参数名简单地进行替换,如果宏的参数名起的不恰当,把你不需要替换掉的东西给替换了,轻则会造成很难发现的编译错误,甚至可能造成代码逻辑的错误,而且宏调试麻烦,很难发现这类错误。比如下面的宏:

 

#define internal_init_addr(addr,machine) ({                     \
    (addr)->sin_family = AF_INET;                               \
    (addr)->sin_addr.s_addr = (machine)->addr.sin_addr.s_addr;  \
    (addr)->sin_port = (machine)->tcp_port;                     \
})

其中addr的类型期望是struct sockaddr_in结构,该结构主要成员是sin_family、sin_addr和sin_port。我在使用这个宏时是用下面的方式:

internal_init_addr(&sa,machine);

那个宏的定义也是没有问题的,而且调用的方式逻辑上也是对的,但是最关键的问题就是替换后出现了问题,替换后成了下面的样子:

({ (&sa)->sin_family = 2; (&sa)->sin_addr.s_addr = (machine)->&sa.sin_addr.s_addr; (&sa)->sin_port = (machine)->tcp_port; });

出问题就出在machine类型中有一个addr成员,而宏的参数中也有一个addr,所以替换时会将(machine)->addr替换成(machine)->&sa,导致编译器报错。所以在定义宏时尽量把你的宏的参数名定义的比较特殊一些,比如以双下划线开始,或者一些特定的字符,以免和宏的内容中的某些东西相同,导致错误的替换。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值