资讯

展开

宏功能的简单使用 几个非常实用的宏技巧

作者:快盘下载 人气:

文章来源;C语言、嵌入式中几个非常实用的宏技巧

宏打印函数

在我们的嵌入式开发中;使用printf打印一些信息是一种常用的调试手段。但是;在打印的信息量比较多的时候;就比较难知道哪些信息在哪个函数里进行打印。

特别是对于异常情况的打印;我们需要快速定位到异常情况的位置。

这时候我们可以使用宏定义来封装一个宏打印函数;这个宏打印函数可以显示打印信息所在的文件、行数、函数名等信息。如;

#define DBG_PRINTF(fmt, args...)  
{
 printf(;<<File:%s  Line:%d  Function:%s>> ;, __FILE__, __LINE__, __FUNCTION__);
 printf(fmt, ##args);
}

使用范例;
宏功能的简单使用 几个非常实用的宏技巧

可见;使用方法与printf的使用方法一样;而且每条打印语句开头都会打印调试信息所在的文件名、行号、函数名信息;方便我们查找一些调试信息。

其中;FILE、LINE、FUNCTION 这三个宏是编译器内置宏定义;分别代表调试信息所在文件、行号、函数。

除此之外;常用的宏还有;DATE、TIME;分别代表当前的编译日期与时间。如;

DBG_PRINTF(;Compile Time: %s  %s
;, __DATE__, __TIME__);

宏技巧
第二条printf中的##符号是为了处理args不代表任何参数的情况。如;

DBG_PRINTF(;Hello world;);

当不加##符号是;以上宏的第二条语句被拓展为;

printf(;Hello world
;, );

可见;多出了一个逗号;这个逗号是多余的。

加上##符号后;以上宏的第二条语句被拓展为;

printf(;Hello world
;);

这才是我们想要的结果。其实这些结果我们通过查看预处理文件可以清晰的知道;
宏技巧
宏技巧
宏技巧

最后需要注意的是;这个DBG_PRINTF还是与printf不一样的。DBG_PRINTF宏是两条语句的组合;无返回值;而printf的原型是;

int printf (const char *__format, ...)

但是我们一般都很少使用printf的返回值;所以DBG_PRINTF的用法与printf函数基本一致。

打印调试宏开关

通常情况下;一些打印调试信息只是在我们调试阶段需要的;在程序发布阶段是不需要的。

所以;为了避免打印调试信息带来的资源开销;我们可以把这些打印调试语句给注释掉。

一种方法是逐句进行注释;这是一种比较低效的方法。比较高效的方法就是添加调试宏开关;利用条件编译来选择打印/不打印调试信息。

比如我们可以把上面的代码改造为;

#define  DEBUG   1  

#if DEBUG
  #define DBG_PRINTF(fmt, args...)  
  {
    printf(;<<File:%s  Line:%d  Function:%s>> ;, __FILE__, __LINE__, __FUNCTION__);
    printf(fmt, ##args);
  }
#else
  #define DBG_PRINTF(fmt, args...)   
#endif

根据DEBUG宏的值来选择对应的打印宏函数。当DEBUG的值为1时启动相关的打印调试语句;DEBUG的值为0时则关闭打印调试语句。

这样我们就可以很方便的通过设置DEBUG宏的值来启动与关闭我们整个工程的DBG_PRINTF打印调试信息。

do{}while(0)

其实;上面我们封装的打印宏DBG_PRINTF还有一点缺陷;比如我们与if、else使用的时候;会有这样的一种使用情况;
宏技巧

此时会报语法错误。为什么呢?

同样的;我们可以先来看一下我们的demo代码预处理过后;相应的宏代码会被转换为什么。如;
宏技巧

这里我们可以看到;我们的if、else结构代码被替换为如下形式;

if(c)
{ /* ....... */ };
else
{ /* ....... */ };

显然;出现了语法错误。if之后的大括号之后不能加分号;这里的分号其实可以看做一条空语句;这个空语句会把if与else给分隔开来;导致else不能正确匹配到if;导致语法错误。

为了解决这个问题;有几种方法。第一种方法是;把分号去掉。代码变成;
宏技巧

第二种方法是;在if之后使用DBG_PRINTF打印调试时总是加{}。代码变成;
宏技巧

以上两种方法都可以正常编译、运行了。

但是;我们C语言中;每条语句往往以分号结尾;并且;总有些人习惯在if判断之后只有一条语句的情况下不加大括号;而且我们创建的DBG_PRINTF宏函数的目的就是为了对标printf函数;printf函数的使用加分号在任何地方的使用都是没有问题的。

基于这几个原因;我们有必要再对我们的DBG_PRINTF宏函数进行一个改造。

下面引入do{}while(0)来对我们的DBG_PRINTF进行一个简单的改造。改造后的DBG_PRINTF宏函数如下;

#define DBG_PRINTF(fmt, args...)  
do
{
    printf(;<<File:%s  Line:%d  Function:%s>> ;, __FILE__, __LINE__, __FUNCTION__);
    printf(fmt, ##args);
}while(0)

这里的do…while循环的循环体只执行一次;与不加循环是效果一样。并且;可以避免了上面的问题。预处理文件;
宏技巧
我们的宏函数实体中;while(0)后面不加分号;在实际调用时补上分号;既符合了C语言语句分号结尾的习惯;也符合了do…while的语法规则。

使用do{}while(0)来封装宏函数可能会让很多初学者看着不习惯;但必须承认的是;这确确实实是一种很常用的方法,在实际应用中;do{}while(0)用的很多。

版权归原作者所有;如有侵权;请联系删除。

加载全部内容

相关教程
猜你喜欢
用户评论
快盘暂不提供评论功能!