一些简单方便易用的C调试用宏
本文目的
最近在写一个 OS 相关的小项目,引入了一个很好用的头文件debug.h
,里面定义了很多方便运行调试的宏,记录和分享一下。注意博主是在 Linux
环境下编程使用到的,至于在 MinGW
和 VS
等环境下能否使用,博主没有尝试过,欢迎讨论。
使用方法
首先定义调试宏以开启调试宏:
#define DEBUG
然后在代码中需要用到的地方调用这个调试头文件里面定义的宏就可以了。
功能列表
我们先列出功能,后面再讨论如何实现的,最后给出源代码。
打印日志到文件
本功能需要在程序中预先打开一个全局文件指针:
FILE *log_fp;
并在程序逻辑中先把文件打开好,打开日志文件的操作建议放到程序中初始化阶段或 main
函数最开始的位置。打开以后,每次只需要调用 Log_write
宏即可方便地打印日志文件了,这个宏方便之处在于可以传入格式化字符串带参数,调用方法:
int var = function();
Log_write("A line to log at %d.", var);
显示日志到屏幕
Log
宏为我们提供了一个方便的手段,用于插入到程序任意地方显示需要打印的消息,并在往屏幕输出时可以一并输出生效的 Log
宏所在的文件、行数和函数名,以及要输出的消息的格式化字符串和参数。下面是一个使用例子:
char *line_read = readline(prompt);
#ifdef DEBUG
Log("Line read: [%s]", line_read);
#endif
条件检验
宏 Assert
用于传入一个验证条件进行断言验证,如果失败,会中止程序运行,并输出一句提示信息,该提示信息作为参数传入即可,使用方法如下:
int i = 11;
Assert(i <= 10, "i is larger than 10.");
像上面的示例代码,如果执行的话会程序会在此中止,并输出
i is larger than 10.
告诉我们这个条件不成立,多用于调试不完善功能时,希望在出错时提前中止程序运行。
无条件中止程序运行
宏 panic
的实质是对上述宏 Assert
的一个封装,让其对 0
这个条件做断言,由于这个条件是永假的,因此该宏用于强制中止程序运行。例如:
int func()
{
// do something
return 0;
panic("Program is not expected to reach here.");
}
假如有什么很意外的情况访问到了这里,可能说明程序实现有问题,此时可以很优雅地处理这种预期外的情况。
尚未实现
宏 TODO()
也是对宏 panic
的一个封装,用于无条件中止程序运行,并输出一句话:
please implement me
表示该功能尚未完成,多用于提醒自己或者他人要实现这个功能。使用的一个例子:
// 该功能模块尚未实现
int func()
{
TODO();
}
浅谈实现
上述实现中用到了几个ANSI C标准中的几个预定义宏,他们有:
__LINE__:在源代码中插入当前源代码行号;
__FILE__:在源文件中插入当前源文件名;
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间;
__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。
注意这些下划线都是双下划线,即输入两个下划线 _
。他们在编译时的预处理阶段,会被替换为相应功能描述中的值,因此完成编译后就有了实际的值可以显示出来。
源代码
将下列代码存为 debug.h
,然后在需要使用的程序的代码文件中引用:
#include "debug.h"
即可使用上述的便捷功能了。
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <stdio.h>
#include <assert.h>
#ifdef DEBUG
extern FILE* log_fp;
# define Log_write(format, ...) \
do { \
if (log_fp != NULL) { \
fprintf(log_fp, format, ## __VA_ARGS__); \
fflush(log_fp); \
} \
} while (0)
#else
# define Log_write(format, ...)
#endif
#define Log(format, ...) \
do { \
fprintf(stdout, "\33[1;34m[%s,%d,%s] " format "\33[0m\n", \
__FILE__, __LINE__, __func__, ## __VA_ARGS__); \
fflush(stdout); \
Log_write("[%s,%d,%s] " format "\n", \
__FILE__, __LINE__, __func__, ## __VA_ARGS__); \
} while (0)
#define Assert(cond, ...) \
do { \
if (!(cond)) { \
fflush(stdout); \
fprintf(stderr, "\33[1;31m"); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\33[0m\n"); \
assert(cond); \
} \
} while (0)
#define panic(format, ...) \
Assert(0, format, ## __VA_ARGS__)
#define TODO() panic("please implement me")
#endif