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

QQ登录

只需一步,快速开始

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

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

[复制链接]

65

主题

113

回帖

1万

积分

用户组: 超级版主

OS与VM研究学者

UID
1043
精华
35
威望
789 点
宅币
8192 个
贡献
1094 次
宅之契约
0 份
在线时间
2034 小时
注册时间
2015-8-15
发表于 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
回复

使用道具 举报

30

主题

202

回帖

2676

积分

用户组: 版主

UID
1821
精华
7
威望
69 点
宅币
2065 个
贡献
206 次
宅之契约
0 份
在线时间
445 小时
注册时间
2016-7-12
发表于 2024-2-15 02:04:02 | 显示全部楼层
如果只是调试输出,早期版本可以这么做,毕竟不支持可变参数宏
  1. #define tr printf("[%s@%d]:",__FILE__ , __LINE__);printf
复制代码
回复 赞! 1 靠! 0

使用道具 举报

9

主题

169

回帖

1万

积分

用户组: 真·技术宅

UID
4293
精华
6
威望
441 点
宅币
8585 个
贡献
850 次
宅之契约
0 份
在线时间
328 小时
注册时间
2018-9-19
发表于 2024-2-6 18:36:00 | 显示全部楼层
Release模式可以考虑直接 #define printf3(fmt, ...) 0 //什么都不输出,直接返回0(因为printf函数返回值是int类型)。
回复 赞! 靠!

使用道具 举报

30

主题

202

回帖

2676

积分

用户组: 版主

UID
1821
精华
7
威望
69 点
宅币
2065 个
贡献
206 次
宅之契约
0 份
在线时间
445 小时
注册时间
2016-7-12
发表于 2024-2-15 01:49:20 | 显示全部楼层
可惜早期c标准不支持可变参宏
回复 赞! 靠!

使用道具 举报

5

主题

22

回帖

251

积分

用户组: 中·技术宅

UID
8534
精华
0
威望
2 点
宅币
220 个
贡献
0 次
宅之契约
0 份
在线时间
42 小时
注册时间
2024-1-14
发表于 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-3-1 05:08 , Processed in 0.042427 second(s), 29 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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