C语言通过宏定义实现便捷打印参数值和参数名

1、格式化打印变量名和值

Python 3.8以上的版本支持f字符串的变量打印功能1,比如:

num = 42
print(f'{num=}')

输出得到:

num=42

这样的方法提高了调试效率。

我觉得这个方法不错,利用C语言的宏定义#var可以将变量转化为字符串,实现类似的效果:

#if LINE_NUMBER
	#define LN printf("(ln: %3d) ", __LINE__)
#else
	#define LN 0
#endif
#define puts(s)    LN + printf("%s\n", s)
#define log_str(x) LN + printf(#x"=\"%s\"\n", x)
#define log_num(x) LN + printf(#x"=%g\n", (float)x)

打印的效果类似如下:

(ln:  7) start!
(ln: 10) s="Hello!"
(ln: 15) n1=1024
(ln: 16) n2=3.14

其中printf函数返回值是int类型,所以可以利用两个printf函数相加,实现在一次printf后再printf一次,通过开关宏定义LINE_NUMBER设置是否打印行号

如果C语言支持C11标准,支持_Generic关键词,还可以将数字、或字符串用同一个宏定义函数打印2。参考代码如下:

#include <stdio.h>

#define u8  unsigned char
#define u16 unsigned short
#define u32 unsigned int
#define u64 unsigned long long

#define s8  signed char
#define s16 signed short
#define s32 signed int
#define s64 signed long long

void log_int(const char* name, int value) {
	printf("%s=%d\n", name, value);
}

void log_float(const char* name, float value) {
	printf("%s=%g\n", name, value);
}

void log_str(const char* name, void* value) {
	printf("%s=\"%s\"\n", name, value);
}

#define log(x) _Generic((x)\
	,u8    : log_int\
	,u16   : log_int\
	,u32   : log_int\
	,s8    : log_int\
	,s16   : log_int\
	,s32   : log_int\
	,float : log_float\
	,double: log_float\
	,u8*   : log_str\
	,s8*   : log_str\
	,char* : log_str\
)(#x, x)

void main() {
	int   n1 = -114;
	float n2 = 5.14;
	char  s1[] = "hello";
	log(n1);
	log(n2);
	log(s1);
}

2、重定向printf

有时候在嵌入式平台上,printf打印需要重定向到串口输出。如果遇到不能使用printf打印,可以使用一种替代的解决方法:

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

#define PRINT_TO_SCREEN 1
#define PRINT_TO_STRING 1
#define PRINT_TO_FILE   1

#define printf log_print

char  log_buff[0x100000]; // 1MB
char *log_pbuff = log_buff;
int   log_reset = 1;

int log_print(char* format, ...)
{
    va_list p;
    va_start(p, format);

#if PRINT_TO_SCREEN
    vprintf(format, p);
#endif

#if PRINT_TO_STRING
    vsprintf(log_pbuff, format, p);
    log_pbuff = strchr(log_pbuff, 0);
#endif

#if PRINT_TO_FILE
    FILE *fp = fopen("log.txt", log_reset ? "w+" : "a+");
    log_reset = 0;
    vfprintf(fp, format, p);
    fclose(fp);
#endif

    va_end(p);
    return 0;
}

通过将printf重定向到log_print函数,使用原有的printf就可以可选地将内容打印到屏幕、字符串、和本地文件

通过初始化全局变量log_reset是否等于1,可以设置每次重启程序前是否清空之前记录文件

使用方法参考:

printf("k=%d\n", 114);
printf("y=%d\n", 514);

运行结束后再次查看buf,字符串会变得丰富:

"k=114\ny=514\n"

需要注意的是,总打印的字符长度不要超过buf申请的空间长度,例如本示例中申请的0x1000001MB空间大小。

本方法前面的宏定义打印参数名+参数值两者互相不冲突,可以同时使用。

3、头文件支持

我将代码整理成了头文件的形式,现在可以通过添加头文件,将调试函数加入代码,在代码一开头加入:

#include "log.h"

就可以使用头文件里面的函数了。文件下载地址:

https://download.csdn.net/download/weixin_39804265/87241255


  1. https://docs.python.org/zh-cn/3.8/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging ↩︎

  2. https://www.zhihu.com/question/555675067/answer/2691305812 ↩︎

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值