找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 320|回复: 4

【C】用可变参宏增强调试输出

[复制链接]
发表于 2024-2-6 15:01:06 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×

前言

在大型项目中使用printf大法进行调试的时候,如果能同时输出所在行,那么分析log就会简单得多。

自制printf

首先自制一个printf,要加上行号和文件名。

#include <stdarg.h>
#include <stdio.h>
#include <string.h>

int vprintf2(const char* src_fn,const int src_ln,const char* format,va_list arg_list)
{
    char buff[512];    // 记得调整缓冲区大小!
    int a=snprintf(buff,sizeof(buff),"[%s@%d] ",src_fn,src_ln);
    int b=vsnprintf(&buff[a],sizeof(buff)-a,format,arg_list);
    // 可以把fwrite替换成别的输出,比如OutputDebugStringA
    fwrite(buff,sizeof(char),a+b,stdout);
    return a+b;
}

int printf2(const char* src_fn,const int src_ln,const char* format,...)
{
    int ret;
    va_list arg_list;
    va_start(arg_list,format);
    ret=vprintf2(src_fn,src_ln,format,arg_list);
    va_end(arg_list);
    return ret;
}

调用printf2就这么写:

printf2(__FILE__,__LINE__,"Hello World!");

但是每次调用printf2都要加上__FILE____LINE__就很蛋疼,而且我们还不能在vprintf2的实现中直接放这两个宏,否则log中的文件名和行号永远是vprintf2这个函数的位置。

宏包装

不过我们可以用宏来包装printf2:

#define printf3(fmt,...)    printf2(__FILE__,__LINE__,fmt,__VA_ARGS__)

这个宏定义在MSVC是没问题的,但是在GCC和clang里用是有坑的。
举个例子,printf3("Hello");在宏展开时会变成printf2(__FILE__,__LINE__,"Hello",);,注意括号前多出了一个逗号。这是因为我们没有使用额外的参数所导致的。而MSVC允许在括号前多出一个逗号,因此仍然能编译通过。
要让GCC和clang对这种情况进行特殊处理,需要改变__VA_ARGS__的使用方式:

#define printf3(fmt,...)    printf2(__FILE__,__LINE__,fmt,##__VA_ARGS__)

虽然GNU说这是GCC的扩展语法,但是MSVC也支持这个语法。因此直接用上述的套路包装printf即可。

安全隐患

在对外发布的时候,程序输出的日志是不可以泄漏源文件的信息的,因此需要用条件编译把printf3包装起来,使得对外发布的版本里不包含源文件信息。

#if defined(_DEBUG)
#define printf3(fmt,...)    printf2(__FILE__,__LINE__,fmt,##__VA_ARGS__)
#else
#define printf3(fmt,...)    printf(fmt,##__VA_ARGS__)
#endif
回复

使用道具 举报

发表于 2024-2-15 02:04:02 | 显示全部楼层
如果只是调试输出,早期版本可以这么做,毕竟不支持可变参数宏
  1. #define tr printf("[%s@%d]:",__FILE__ , __LINE__);printf
复制代码
回复 赞! 1 靠! 0

使用道具 举报

发表于 2024-2-6 18:36:00 | 显示全部楼层
Release模式可以考虑直接 #define printf3(fmt, ...) 0 //什么都不输出,直接返回0(因为printf函数返回值是int类型)。
回复 赞! 靠!

使用道具 举报

发表于 2024-2-15 01:49:20 | 显示全部楼层
可惜早期c标准不支持可变参宏
回复 赞! 靠!

使用道具 举报

发表于 2024-2-19 13:55:35 | 显示全部楼层
刚看到https://www.zhihu.com/question/552041042
说 C23 支持这样写:
  1. #define debug(format, …) fprintf(stderr, format __VA_OPT__(,) __VA_ARGS__)
复制代码
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-10-8 16:17 , Processed in 0.033536 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表