浅谈宏函数妙用!

嵌入式技术

1372人已加入

描述

宏函数在项目开发中用的频率非常高,跟普通函数相比,它没有复杂的调用步骤,也不需要给形参分配空间,所以很多场景都需要宏函数的存在。

代码  

简单的宏函数确实也挺简单,比如这样的无参宏函数,在代码中凡是出现debug的地方,都会替换成printf这条输出语句。

#define debug printf("helloworld
")
void main()
{
    debug;  // 等价于 printf("helloworld");
}
  复杂一点的,在宏函数中加个参数,我们把它称作有参宏函数,比如这样的:
#define debug(s) printf("%s
", s)
void main()
{
    debug("helloworld
");
}
调用debug的时候,需要传个参数进去,当然这个参数必须是字符串,如果随便写个数字,运行的时候就是段错误。
#define debug(s) printf("%s
", s)
void main()
{
    debug(1);    //段错误
}
  这也把宏函数的缺点暴露了出来,参数没有类型限制,不够安全。 再回到文章刚开始的地方。

代码  

这个宏函数不仅有参,而且还是可变参数,在代码中凡是出现debug的地方,都把他替换成fprintf。 唯一不太好懂的地方,可能是args前面出现了两个井号。 两个井号在C语言中被称为连接符号,功能就是在带参的宏函数中将两个字串连接成一个新的字符串。 举个例子,有这样一个宏函数:
#define  name(x)  name_##x
void main()
{    
    int name_1, name_2;    
    name(1);        // 等价于 name_1;
}
  如果调用的时候参数传入1,就被替换成了name_1。 在可变参数中,两个井号就是把所有参数连接在后面。 宏函数的使用场景很多,就拿图上这个来说,可以实现项目开发的时候打开调试信息,方便调试代码。项目完成后关闭调试信息。我们来个测试代码。
#include 


#ifdef DEBUG 
    #define debug(format, args...) fprintf(stderr, format, ##args)
#else
    #define debug(format, args...) 
#endif


int main()
{
    int a = 1;


    debug("a = %d
", a); 


    return 0;
}
在主函数中调用debug函数,如果你希望debug函数执行,编译的时候提供DEBUG宏定义就行。
gcc test.c -o test -DDEBUG
  如果你不希望信息输出,编译的时候就不要管它。
gcc test.c -o test
  这个方法比项目完成后,一行一行去删除调试信息来的更方便。 如果你看过一些开源代码,肯定会发现很多宏定义中使用do while语句。
#define NS_GET16(s, cp) do { 
    const unsigned char *t_cp = (const unsigned char *)(cp); 
    (s) = ((uint16_t)t_cp[0] << 8) 
        | ((uint16_t)t_cp[1]) 
        ; 
    (cp) += NS_INT16SZ; 
} while (0)


#define NS_GET32(l, cp) do { 
    const unsigned char *t_cp = (const unsigned char *)(cp); 
    (l) = ((uint32_t)t_cp[0] << 24) 
        | ((uint32_t)t_cp[1] << 16) 
        | ((uint32_t)t_cp[2] << 8) 
        | ((uint32_t)t_cp[3]) 
        ; 
    (cp) += NS_INT32SZ; 
} while (0)
  虽然看不懂,但还是觉得这段代码写的非常厉害,那你知道为什么要这样写吗? 1.可以避免空的宏定义出现warning。
#define foo()
 

 

有些编译器对这样的代码会提示警告,do while可以消除警告。

 

#define foo() do {}while(0)

 

2.作为一个独立的单元,可以定义变量或者是进行更复杂的运算。

 

#define debug do { 
    int a;
    printf("helloworld"); 
    foo(); 
}while (0)

 

3.放在判断语句中,避免语法错误。

 

#include 


#define debug(num)  num--; printf("%d
", num)


int main()
{
    int num = 1;


    if (num > 0)
        debug(num);
    else 
        printf("error
");


    return 0;
}

 

这个代码编译的时候会提示语法错误,因为宏定义中包含了两条语句,同时判断语句中又没有使用大括号。do while可以解决这个问题。

 

#include 


#define debug(num)  do {num--; printf("%d
", num);} while(0)


int main()
{
    int num = 1;


    if (num > 0)
        debug(num);
    else 
        printf("error
");


    return 0;
}

 

总结一下,do while可以把复杂的语句包裹起来,使它成为一个单独的单元,避免语法问题。而且大部分的编译器都能识别while(0)这种无效的循环,并且把它优化掉,不会造成效率上的问题。

  审核编辑:汤梓红

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分