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


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

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

    num = 42
    print(f'{num=}')
    
    • 1
    • 2

    输出得到:

    num=42
    
    • 1

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

    我觉得这个方法不错,利用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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    打印的效果类似如下:

    (ln:  7) start!
    (ln: 10) s="Hello!"
    (ln: 15) n1=1024
    (ln: 16) n2=3.14
    
    • 1
    • 2
    • 3
    • 4

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

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

    #include 
    
    #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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    2、重定向printf

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

    #include 
    #include 
    #include 
    
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

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

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

    使用方法参考:

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

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

    "k=114\ny=514\n"
    
    • 1

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

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

    3、头文件支持

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

    #include "log.h"
    
    • 1

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

    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 ↩︎

  • 相关阅读:
    使用cpolar发布群晖NAS上的网页(1)
    WPF 入门笔记 - 05 - 依赖属性
    可变长子网划分
    DM3730 uboot 分析
    PLGA纳米粒负载环丙沙星Ciprofloxacin-PLGA|甲硝唑修饰PLGA纳米粒PLGA-Metronidazole
    php的错误机制
    管道-有名管道
    交叉验证如何防止过拟合
    Maven setting配置文件
    数据结构——栈
  • 原文地址:https://blog.csdn.net/weixin_39804265/article/details/127802014