巧用宏进行程序中的调试

8-8程序调试的debug 8.8.1 程序调试的常见方案:单步调试、裸机LED调试、打印信息、log文件 (1)利用调试器进行单步调试(譬如IDE中,Jlink)适用于新手,最大的好处就是直观,能够帮助找到问题。缺点是限制性大、速度慢。 (2)裸机使用LED、蜂鸣器等硬件调试,适合单片机裸机程序; (3)printf函数打印调试,比较常用,作为程序员必须学会打印调试。好处是具有普遍性,几乎在所有的情况下都能用。 (4)log文件(日志文件)是系统运行过程中在特定的时候打印一些调试信息,日志文件记录下来这些调试信息以后以供后续查找追查 阅读详情

转载的两篇是目前我见过很容易理解且也很详细的文章了

转载巧用可变参数宏、编译器内置宏和printf输出调试信息

前言:

我们在写程序的时候,总是或多或少会加入一些printf之类的语句用于输出调试信息,但是printf语句有个很不方便的地方就是当我们需要发布程序的时候要一条一条的把这些语句删除,而一旦需要再次调试的时候,这些语句又不得不一条条的加上,这给我们带来了很大的不便,浪费了我们很多的时间,也造成了调试的效率低下。所以,很多人会选择使用宏定义的方式来输出调试语句。

比如,定义一个宏开关:

#define __DEBUG

当需要调试的时候,使用语句:

#ifdef __DEBUG

printf(xxx);

#endif

这种方式的调试,可以通过undef __DEBUG的方式让告知编译器不编译这些语句,从而不再输出这些语句。但是这种方式的麻烦之处也是显而易见的,每一条调试语句都需要使用两条宏定义来包围,这不但在代码的编写上不便,源码结构也不好看,工作量依然不小。

如果我们能够把这三条语句编程一条,那该多舒服呀~,于是,我们想到使用这样的语句:

#ifdef __DEBUG

#define DEBUG(info) printf(info)

#else

#define DEBUG(info)

#endif

这样,我们在编写代码的时候,使用DEBUG一条语句就可以了,我们把宏开关__DEBUG打开,所有的DEBUG(info)宏定义信息都会被替换为printf(info),关上则会被替换成空,因此不会被编译。嗯,这次方便多了,一条语句就可以了~~~ 但是,问题也随之而来了,printf是支持多个参数的,而且是不定参数,当你使用下面这样的语句时就会报错:

DEBUG(“%s”,msg)

这是因为,DEBUG(info)这条宏定义只支持一个参数的替换。

因此,我们希望DEBUG能够像printf那样,支持多个参数,并且这些参数刚好展开成为printf语句本身使用的参数,譬如:我们希望DEBUG(“%s”,msg)能够展开为printf(“%s”,msg)

正文:

通过网上的资料查阅,发现自C99规范以后,编译器就开始支持不定参数的宏定义,就像printf一样。

大家可以看看这篇文章:
http://blog.csdn.net/aobai219/archive/2010/12/22/6092292.aspx

(这个链接也转的,我已经找不到原始作者到底是谁了,唉,互联网啊。。。)

于是,我们定义了一个这样的东东:

#define DEBUG(format, …) printf (format, ##VA_ARGS)(’ ## ‘的意思是,如果可变参数被忽略或为空,将使预处理器( preprocessor )去除掉它前面的那个逗号。)

于是乎,我们神奇地发现,DEBUG完全取代了printf,所有的DEBUG(…)都被完成的替换成了printf(…),再也不会因那个可恶的逗号而烦恼了。

但是,我们发现,光有printf还不够,虽然调试信息是输出了,可是很多的调试信息输出,我们并不能一下子知道这条信息到底是在那里打印出来的,于是,我们又想,能不能把当前所在文件名和源码行位置也打印出来呢,这样不就一目了然了吗,哪里还用的着去想,去找调试信息在哪里输出的呢,都已经打印出来了!

于是我们就有了下面的故事。。。

编译器内置宏:

先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。

ANSI C标准中有几个标准预定义宏(也是常用的):

__LINE__:在源代码中插入当前源代码行号;

__FILE__:在源文件中插入当前源文件名;

__DATE__:在源文件中插入当前的编译日期

__TIME__:在源文件中插入当前编译时间;

__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;

__cplusplus:当编写C++程序时该标识符被定义。

编译器在进行源码编译的时候,会自动将这些宏替换为相应内容。

看到这里,你的眼睛应该一亮了吧,嗯,是的,FILELINE正是我们前面想要的输出的,于是,我们的每一条语句都变成了:

DEBUG(“FILE: %s, LINE: %d…”,FILE,LINE,…)

其实没有必要,FILE本身就会被编译器置换为字符常量,于是乎我们的语句又变成了这样:

DEBUG(“FILE:”FILE“, LINE: %d…”,LINE,…)

但是,我们还是不满足,依然发现,还是很讨厌,为什么每条语句都要写”FILE:”FILE“, LINE: %d 以及,__LINE,这两个部分呢?这不是浪费我们时间么?

哈哈,是的,这就是本次大结局,把DEBUG写成这样:

DEBUG(format,…) printf(“FILE: “FILE“, LINE: %d: “format”/n”, LINE, ##VA_ARGS)

没错,就是这样!下面,所有的DEBUG信息都会按照这样的方式输出:

FILE: xxx, LINE: xxx, …….

最后coding测试

//============================================================================
// Name : debug.cpp
// Author : boyce
// Version : 1.0
// Copyright : pku
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <stdio.h>

#define __DEBUG__

#ifdef __DEBUG__
#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"\n", __LINE__, ##__VA_ARGS__)
#else
#define DEBUG(format,...)
#endif

int main(int argc, char **argv) {
    char str[]="Hello World";
    DEBUG("A ha, check me: %s",str);
    return 0;
}

转载 Linux程序开发打印Debug信息的使用技巧
少废话,coding 最重要的是如何debug,debug 当然就少不了把程序信息输出,如何清晰明了地打印出程序信息,可以快速判断程序运行情况,定位程序出问题的地方。先来一段代码实战下再说:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#ifndef __USE_DEBUG
#define __USE_DEBUG

#define USE_DEBUG
#ifdef USE_DEBUG
#define DEBUG_LINE() printf("[%s:%s] line=%d\r\n",__FILE__, __func__, __LINE__)
#define DEBUG_ERR(fmt, args...) printf("\033[46;31m[%s:%d]\033[0m "#fmt" errno=%d, %m\r\n", __func__, __LINE__, ##args, errno, errno)
#define DEBUG_INFO(fmt, args...) printf("\033[33m[%s:%d]\033[0m "#fmt"\r\n", __func__, __LINE__, ##args)
#else
#define DEBUG_LINE()
#define DEBUG_ERR(fmt, ...)
#define DEBUG_INFO(fmt, ...)
#endif

#endif

void func()
{
        DEBUG_LINE();
        DEBUG_INFO("Garfield test DEBUG_INFO() d: %d ; s: %s", 1 , __FUNCTION__);
        DEBUG_ERR("Garfield test DEBUG_ERR() d: %d ; s: %s", 2 , __FUNCTION__);
}

int main(int argc,char **argv)
{
        func();

        return 0;
}

分析:
1, 使用颜色打印调试信息:

printf("\033[46;31m[%s:%d]\033[0m "#fmt" errno=%d, %m\r\n", __func__, __LINE__, ##args, errno, errno);

上面printf时在Linux 命令行下打印出带颜色的字体,方便一眼区分不同种类的调试信息,只需要加上一些颜色代码,例如:这里的46代表底色, 31代表字体的颜色。
使用ascii code 是对颜色调用的始末格式如下:

\033[ ; m …… \033[0m

后面哪个 ”\033[0m” 是对前面哪个颜色载入的结束,恢复到终端原来的背景色和字体色,可以把后面哪个修改成如下试试:

#define DEBUG_ERR(fmt, args...) printf("\033[46;31m[%s:%d]\033[40;37m "#fmt" errno=%d, %m\r\n", __func__, __LINE__, ##args, errno, errno);

下面列出 ascii code 的颜色值:
字背景颜色范围:40—-49 字颜色:30———–39
40:黑 30:黑
41:深红 31:红
42:绿 32:绿
43:黄色 33:黄
44:蓝色 34:蓝色
45:紫色 35:紫色
46:深绿 36:深绿
47:白色 37:白色

打印调试信息的跟踪位置:

printf("[%s:%s] line=%d\r\n",__FILE__, __func__, __LINE__);
printf("\033[33m[%s:%d]\033[0m "#fmt"\r\n", __func__, __LINE__, ##args);

如上代码:
1)__FILE__ 打印出调试信息所在的文件名;
2)__func__ 将会打印出调试信息所在的函数名;
3)__LINE__ 将会打印出调试信息所在文件的行号;

使用不定参数向打印信息里面加入自己想看到的调试信息:

#define DEBUG_INFO(fmt, args...) printf("\033[33m[%s:%d]\033[0m "#fmt"\r\n", __func__, __LINE__, ##args);

调用方式如下:

int i = 110;
char * s = “hello world!”;
DEBUG_INFO("Garfield test DEBUG_INFO() d: %d ; s: %s", i , s);

至于不定数量参数宏与不定参数函数的使用就没神马好说的啦,自己去google吧!

下面引用一位大侠的blog,列出一些常用的debug 语句:
出自:http://blog.mcuol.com/User/luoming/Article/16499_1.htm

#ifdef DEBUG
#define F_OUT printf("%s:", __FUNCTION__);fflush(stdout);
#define L_OUT printf("%s:%d:", __FILE__, __LINE__);fflush(stdout);
#define A_OUT printf("%s:%d:%s:", __FILE__, __LINE__, __FUNCTION__);fflush(stdout);
#define D_OUT printf("DEBUG:");fflush(stdout);

#define F_PRINTF(fmt, arg...) F_OUT printf(fmt, ##arg)
#define L_PRINTF(fmt, arg...) L_OUT printf(fmt, ##arg)
#define A_PRINTF(fmt, arg...) A_OUT printf(fmt, ##arg)
#define PRINTF(fmt, arg...) D_OUT printf(fmt, ##arg)
#define DBUG(a) {a;}
#else
#define F_OUT
#define L_OUT
#define A_OUT
#define D_OUT

#define F_PRINTF(fmt, arg...)
#define L_PRINTF(fmt, arg...)
#define A_PRINTF(fmt, arg...)
#define PRINTF(fmt, arg...)
#define DBUG(a)
#endif

#define F_PERROR(fmt) F_OUT perror(fmt)
#define L_PERROR(fmt) L_OUT perror(fmt)
#define A_PERROR(fmt) A_OUT perror(fmt)
#define PERROR(fmt) D_OUT perror(fmt)
方法:使用定义,实现调试信息的可控输出 0.目标 使用定义,实现调试信息可控输出。可对调试信息进行等级分类,并分级输出调试信息,在嵌入式操作系统环境下(如VxWorks),还可以根据实际需求,打开或关闭对应等级的调试信息,方便bug排查。 1.代码 #include <stdio.h> #define DBG_CAN_INFO g_dbg_can_info_level #define DBG_TCP_INFO g_dbg_tcp_info_level #define DBG_UDP_INFO g_dbg_udp_info_leve 阅读详情

相关推荐

caxa二次开发实例展示

本人有8年CAXA各产品(如电子图版、工艺图表、PDM等)二次开发经验,参与过国家电网南瑞集团、中石油、龙记集团、欧姆龙集团、港日集团、中集集团、中国五矿、方圆塑机、大连锅炉、金蝶PDM集成CAXA,TEAMCENTER集成CAXA、大连锅炉机械、中国中车等项目的开发,擅长CAXA电子图版、CAXA工艺图表的二次开发,有需要的可以联系我,电话:13169467685、13249738576 Q

hourui800720的专栏 7987

调试信息函数的封装实用小技巧分享

如果有需求,还可以在中定义一个输出等级,do{}while(0)中判断一下输出等级再决定是否输出调试信息。在轻量型嵌入式开发中,没有单独的日志信息库,所以通常需要开发者自己对调试打印信息进行封装,这样即方便调试,也方便后续查问题,可以用一个开关对齐全局控制。另外就是在打印信息中,我们想要加入其他打印的信息,比如行号,函数名等等,但是又不想每写一句调试信息就写一边,就可以通过下面的方式,利用的可变参数实现。如代码段一,用去简单粗暴的定义,定义后编译后显示的占的空间很小,会直接被优化掉。

qq_37200742的博客 246

微信商家转账报错“当前使用用户过多,请稍后重新确认收款”解决方案

当前使用用户过多,请稍后重新确认收款”这个错误,其实就是典型的参数误处理导致的假报错。记住一句话就行👇JSAPI 场景下的package参数不要做 UrlEncode。如果你也遇到这个问题,希望这篇文章能帮你少踩一个坑。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 721

编译器中一些内置(方便调试

ANSI C标准中几个标准预定义: __LINE__:在源代码中插入当前源代码行号; __FILE__:在源文件中插入当前源文件名; __DATE__:在源文件中插入当前的编译日期 __TIME__:在源文件中插入当前编译时间; __STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1; __cplusplus:当编写C++程序时该标

高原 1158

C/C++调试

转载地址:http://blog.chinaunix.net/uid-24866372-id-2128907.html 调试信息的输出方法有很多种, 例如直接用printf, 或者出错时使用perror, fprintf等将信息直接打印到终端上, 在Qt上面一般使用qDebug,而守护进程则一般是使用syslog将调试信息输出到日志文件中等等。 使用标准的方法打

linuxweiyh的博客 5209

巧用可变参数、编译器内置和printf输出调试信息

前言:我们在写程序的时候,总是或多或少会加入一些printf之类的语句用于输出调试信息,但是printf语句有个很不方便的地方就是当我们需要发布程序的时候要一条一条的把这些语句删除,而一旦需要再次调试的时候,这些语句又不得不一条条的加上,这给我们带来了很大的不便,浪费了我们很多的时间,也造成了调试的效率低下。所以,很多人会选择使用定义的方式来输出调试语句。比如,定义一个开关:#define _

大道至简,持之以恒 1万+

8.6 的运行和调试

创建之后,使用之前要先进行调试,以保证运行与设计者的要求一致,调试无误后就可以运行了。

复行数十步 2042

【嵌入式】C语言程序调试使用的技巧

00. 目录 文章目录00. 目录01. 调试相关的02. # 字符串化操作符03. ## 连接操作符04. 调试第一种形式05. 调试的第二种定义方式06. 对调试语句进行分级审查07. 条件编译调试语句08. 使用do...while的定义09. 代码剖析10. 附录 01. 调试相关的 在Linux使用gcc编译程序的时候,对于调试的语句还具有一些特殊的语法。gcc编译的过程中,会生成一些,可以使用这些分别打印当前源文件的信息,主要内容是当前的文件、当前运行的函数和当前的程序行。 具体

C/C++软件工程师、嵌入式软件工程师、物联网研发工程师、C/C++讲师、物联网讲师、嵌入式讲师、鸿蒙讲师---欢迎大家一起交流(私信添加博主微信) 4158

Visal C++ 中用于调试程序的几个

   一、TRACE  当选择了Debug目标,并且afxTraceEnabled变量被置为TRUE时,TRACE也就随之被激活了。但在程序的Release版本中,它们是被完全禁止的。下面是一个典型的TRACE语句:  int nCount =9;  Cstring strDesc("total");  TRACE("Count =%d,Description =%s/n",nCount,

蜡笔小辛的专栏 1255

C语言之顺序结构以及程序调试的debug

C语言中顺序结构和程序调试的debug

tmk1234567890的博客 1346

TRACE程序调试中的使用

  若要在调试器的“输出”窗口中显示来自程序的消息,可以使用 ATLTrace 或 MFC Trace 。TRACE只在程序的“Debug”版本中起作用,在“Release”版本中编译时将消失。  下面的示例显示几种 TRACE 的用法。与 printf 类似,TRACE 可处理许多参数。int x = 1;int y = 16;float z = 32.0;TRACE(

朱铭雷的日记本 1286

C语言中程序调试使用技巧

1)打印文件,函数和程序行         在linux使用GCC编译程序的时候,gcc在编译的过程中,会生成一些,可以使用这些分别打印当前源文件的信息。主要有当前文件(__FILE__,为字符串型char *),当前运行的函数(__FUNCTION__,为字符串型char *)和当前的程序行(__LINE__,为整型int)。使用实例如下: printf("file:%s

__清__风__ 1376

C++开发基础之调试的理解和应用

欢迎关注dotnet研习社,今天我们讨论的话题是:调试的理解和应用。在 C++ 开发中,调试是不可避免的工作。无论是开发新功能、修复 bug,还是优化代码,良好的调试信息都能够帮助开发者迅速定位问题。在这方面,编译器提供了一些强大的预定义,如__FILE____LINE____DATE____TIME__,它们可以帮助我们记录程序执行的文件、行号、函数名,甚至编译时间等信息,为调试提供了很多便利。在 C++ 编程中,调试信息的清晰和详尽能够大大提高开发效率。通过使用__FILE____LINE__

dotnet研习社 1112

调试技巧MFC中TRACE的使用

个人总结:最近看网络编程是碰到了TRACE语句,不知道在哪里输出,查了一晚上资料也没找出来,今天终于在CSDN上找到了,真是个高地方啊,方法如下: 1.在MFC中加入TRACE语句 2.在TOOLS->MFC TRACER中选择 “ENABLE TRACING”点击OK 3.进行调试运行,GO(F5)(特别注意:不是执行‘!’以前之所以不能看到TRACE内容,是因为不是调试执行,而是‘!’了

ghevinn欢迎您光临 1万+

程序调试的DEBUG

debug的实现原理 (1)DEBUG大概的原理是: #ifdef DEBUG #define dbg() printf() #else #define dbg() #endif (2)工作方式是:如果我们要输出DEBUG版本则在条件编译语句前加上#define DEBUG即可,这样程序中的调试语句dbg()就会被替换成printf从而输出;如果我们要输出RELEASE版本则去掉#define DEBUG,则dbg()就会被替换成空,则程序中所有的dbg()语句直接蒸发了,这样的程序编译时就会生成没有

weixin_54989626的博客 1148

C++ 预处理与相关编程(#,##等等)

一、简介引言 C++模板元编程(template metaprogramming)虽然功能强大,但也有局限性: 不能通过 模板展开 生成新的标识符(identifier)。例如:生成新的 函数名、类名、名字空间名 等 使用者 只能使用 预先定义的标识符,不能通过 模板参数 获取符号/标记(token)的字面量(literal) 例如 在反射中获取 实参参数名的字面量,在断言中获取 表达式的字面量。 所以,在需要直接操作标识符的情况下,还需要借助,进行预处理阶段的元编程: ...

smilejiasmile的博客 1107

预处理器进行调试

先介绍几个编译器内置的定义,这些定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。 ANSI C标准中有几个标准预定义(也是常用的): __LINE__:在源代码中插入当前源代码行号; __FILE__:在源文件中插入当前源文件名; __DATE__:在源文件中插入当前的编译日期 __TIME__:在源文件中插入当前编译时间; ...

angtuanv07384的博客 192

TRACE ASSERT VERIFY

一、TRACE 当选择了Debug目标,并且afxTraceEnabled变量被置为TRUE时,TRACE也就随之被激活了。但在程序的Release版本中,它们是被完全禁止的。下面是一个典型的TRACE语句:    …        int nCount =9;        CString strDesc("total");        TRACE("Count =%d,Descripti

looob的专栏 925

STM32(3日学习总结|debug_log的处理)

这是在视频中学大佬在大佬那边学来的一个知识 当遇到要随时产生信息到串口,方便调试 并且要求能定位:文件,类型,自定义文本,变量输出 注意事项: 这个需要配合printf重定向的使用(可以观看我的另一篇关于printf的处理) 在使用前需要在main.h处进行包含 设计知识:可变参数,##黏贴等处理,非常牛逼 debug_log.h // // Created by o.o on 2022/1/19. // #ifndef CHECK_DEBUG_LOG_H #define C..

weixin_60742599的博客 1171

串口信息打印规范(含打印技巧)

方法二中,若#define USER_MAIN_DEBUG,则使用user_main_printf();可以直接打印你所需的信息,而且自带\r\n,若未进行定义,使用对应函数则打印空白。在每一段串口打印的程序段中,加入#if Log和#endif,开启/屏蔽打印段仅需修改Log的值,为1/0-->打印/不打印信息。这里的串口打印使用printf函数,这里使用了printf重定向(借鉴的)日志级别:info(初始化)、debug(运行过程)、error(报错)[日志级别] 文件名 : 日志信息。

a2001444的博客 3389

powerBuilder编绎成DLL格式需要的EN32T.H头文件

powerBuilder编绎成DLL格式需要的EN32T.H头文件PB编绎成DLL文件时,提示"Error opening file 'c:\windows\system32\cgen\en32t.h'" 这个错误的解决方法下载这个文件后,说明看我的博文:http://blog.csdn.net/aasmfox/archive/2010/12/25/6097561.aspx希望对需要的菜鸟有所帮助哈哈,想不到真的有人下载这个东西。还是用PB12的版本好啦。

上一篇: 条件编译#ifdef的用法
下一篇: Linux df和du的区别
黑帽子和猫
博客等级 码龄12年 41粉丝 121原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值