C语言中几种输出调试信息的方法

发布 2019-07-16 13:45:00 阅读 7021

在调试程序时,输出调试信息是一种普遍、有效的方法。输出调试信息一般有以下五种方法:

方法一:直接使用屏幕打印函数printf。

该方法直接在需要输出调试信息的位置使用函数printf输出相应的调试信息,以及某些关键变量的值。我们通过以下求阶层的函数fact来看看该方法的调试程序过程。

#include <>

int fact(int n)

int i,f=1;

for( i=1; i<=n; i++)

return f;

int main()

printf( "4!=%d/n", fact(4) )

return 0;

程序1: 有bug的求阶层函数。

程序1编译运行的结果如下:

结果错误。为了找到结果错误的原因,我们在语句"f +=i;"之后插入函数printf输出调试信息,如程序2。

#include <>

int fact(int n)

int i,f=1;

for( i=1; i<=n; i++)

return f;

int main()

printf( "4!=%d/n", fact(4) )

return 0;

程序2: 加入函数printf输出调试信息的求阶层函数。

再编译运行该程序,屏幕输出如下:

i=1 ; f=2 i=2 ; f=4 i=3 ; f=7 i=4 ; f=11 4!=11

原来语句"f +=i"错了,应该为"f *=i"。修改过来(见程序3),再编译运行,结果如下:

i=1 ; f=1 i=2 ; f=2 i=3 ; f=6 i=4 ; f=24 4!=24#include <>

int fact(int n)

int i,f=1;

for( i=1; i<=n; i++)

return f;

int main()

printf( "4!=%d/n", fact(4) )

return 0;

程序3: 修改正确的求阶层函数。

调试完成,bug找到,并修改正确。然后将加入的调试的函数printf 删除或注释掉。

该方法的缺点是(1)在正式发布的程序中需要去除或注释掉这些调试语句;(2)若程序又出现bug,则又需要重新插入函数printf输出调试信息,造成工作的重复。

方法二:自定义调试函数debug。

为了避免方法一的缺点,可以利用条件编译技术,如程序4自定义调试函数debug。当程序正式发布的编译时取消宏定义__debug__,在正式发布的程序中就不会输出调试信息。若又出现bug,只要重新在编译程序时定义宏__debug__即可恢复原来的调试信息输出。

可以在编写程序时就有目的事先插入些调试语句,这将有益于调试程序。另外,可以根据需要编写函数debug,将调试信息输出到除屏幕以外的其它地方,如文件或syslog服务器等。

#include <>

#ifdef __debug__

#include <>

void debug(const char *fmt, .

va_list ap;

va_start(ap, fmt);

vprintf(fmt, ap);

va_end(ap);

#elsevoid debug(const char *fmt, .

#endif

int fact(int n)

int i, f = 1;

for( i=1; i<=n; i++)

return f;

int main()

printf( "4!=%d/n", fact(4) )

return 0;

程序4: 自定义调试函数debug

该方法的缺点是(1)调试信息要么全部输出,要么全不输出;(2)要重新输出调试信息时需要重新编译程序。

方法三:含调试等级的自定义调试函数debug。

可以继续改进方法,避免方法二中的缺点。我们可以根据调试信息的细节程度,将调试信息分成不同的等级。调试信息的等级必须大于0,若调试信息细节程度越高, 则等级越高。

在输出调试信息时,若调试等级高于调试信息等级才输出调试信息,否则忽略该调试信息,如程序5。当调试等级为0时,则不输出任何调试信息。

#include <>

#include <>atoi()

#include <>

int debug_level;

void debug(int level, const char *fmt, .

if( level <=debug_level )

int fact(int n)

int i, f = 1;

for( i=1; i<=n; i++)

return f;

int main(int argc, char *ar**)

if ( argc < 2 )

elseprintf( "4!=%d/n", fact(4) )

return 0;

程序5: 含调试等级的自定义调试函数debug

用命令"gcc -wall -o fact "编译程序5,得到可执行文件 fact。若需要输出调试信息,只需要指定调试等级不低于250即可,如运行命令"./fact 250",否则将不会输出调试信息。

这样,在正式发布版中包含调试信息也无伤大雅了,因为只需将调试等级配置为0,将不会出现任何调试信息。

该方法的缺点是效率不太高,因为不管调试信息是否需要输出,都会进行一次函数调用。若不需要输出调试信息,这次函数调用就多余了。

方法四:调试等级的判断放在自定义调试函数debug之外。

为了减少不必要的函数调用,可以用宏定义将调试等级的判断放在函数debug之外,如程序6。

#include <>

#include <>atoi()

#include <>

int debug_level;

#define debug(level, fmt, arg...

if( level <=debug_level ) debug(fmt, ##arg)

void __debug(const char *fmt, .

va_list ap;

va_start(ap, fmt);

vprintf(fmt, ap);

va_end(ap);

int fact(int n)

int i, f = 1;

for( i=1; i<=n; i++)

return f;

int main(int argc, char *ar**)

if ( argc < 2 )

elseprintf( "4!=%d/n", fact(4) )

return 0;

程序6: 调试等级的判断放在自定义调试函数debug之外。

这种方法对于不需要输出的高等级的调试信息操作来说,仅仅多了个两个整数之间的大小判断。在正式的程序运行时,效率是有所提高的。

但这种调试信息输出的方法依然不够完美。对于一个大项目,一般分为若干个模块,bug将会定位到某个或某几个模块。若整个项目的调试信息都输出,信息量将会非常大,也容易干扰调试人员的思维。

这时,我们需要的是只输出我们关心的那些模块的调试信息,但该方法并不能达到我们的要求。它只能根据调试等级输出信息,对于同一调试等级的信息要么全输出,要么全不输出。

方法五:根据不同的功能模块分别定义不同的调试等级。

在squid[1]中,定义了以下的功能模块调试等级变量和调试函数:

int debuglevels[max_debug_sections];

#define debug(section, level) /

((_db_level = level)) debuglevels[section]) void) 0 : db_print

然后在程序中如下使用它:

debug(17, 3) (fwdstatefree: %p/n", fwdstate);

上述调试函数很灵活,可以在不同的模块中定义有不同的调试等级,当需要调试某功能时,只需将该模块的调试等级定义为相应的等级,就可输出需要的调试信息。

根据方法五的思想,本人编写了my_见程序7)和my_ 文件(见程序8)。该文件可以应用于c语言程序中,支持根据不同的功能模块分别定义不同的调试等级。

#ifndef my_debug_h

#define my_debug_h

#include <>

/ 模块功能号。

enum {

my_section_fact = 0,my_section_nnn1,my_section_nnn2,my_section_nnnn,my_section_end,;

/ 非my_文件的外部变量声明。

#ifndef my_debug_c

extern int __my_allow_debug_levels[my_section_end];

#endif

/ (内部使用) 判断"section"模块功能号是否允许"debug_level"等级的调试信息输出。

#define __my_unallow_debug(section, debug_level) /

debug_level > my_allow_debug_levels[section] )

/ (内部使用) 调试信息输出函数。

#define __my_debug(format, arg...

printf("%s:%d %s: "format, _file__,line__,function__,##arg)

/ 初始化"section"模块功能号的调试等级。

#define my_init_debug_levels(section, allow_debug_level) /

__my_allow_debug_levels[section] =allow_debug_level )

/ 调试信息输出函数,该信息为"section"模块功能号"debug_level"等级的调试信息。

#define my_debug(section, debug_level) /

( _my_unallow_debug(section, debug_level) )void) 0 : my_debug

#endif //my_debug_h

程序7: my_

c语言printf 输出格式大全

printf的格式控制的完整格式 0 l或h 格式字符。下面对组成格式说明的各项加以说明 表示格式说明的起始符号,不可缺少。有 表示左对齐输出,如省略表示右对齐输出。0 有0表示指定空位填0,如省略表示指定空位不填。指域宽,即对应的输出项在输出设备上所占的字符数。n指精度。用于说明输出的实型数的小数...

c语言中宏的高级使用技巧

1 前言。c语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简单的字符串替换,根据是否带参数分为无参和带参。宏的简单应用很容易掌握,今天主要总结一下宏的特殊符号及惯用法。1 宏中包含特殊符号 2 宏定义用dowhile 0 2 特殊符号 when you put a befor...

作文语言中的超常搭配

作者 林勇。语文教学与研究 教研天地 2013年第12期。随手翻开一本 读者 2013年第17期 随处可见这样的标题,在心里点一盏小灯 光阴的故事里,你是最刻骨的那一篇 喜宴的江湖 作者有意把两个不相干的词语糅合在一起,让灯 在心上点亮 让人 成为岁月中的一篇故事 让 江湖的是是非非进入宴请之中 既...