C语言打印程序行号、日期方便调试程序

659 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

一、前言

平时开发C语言程序时,经常需要调试代码,C语言有一些宏,可以打印出当前的行号、文件名称、日期、时间,对程序的调试起到很大的帮助,可以快速定位问题。特别是开发单片机程序时,使用这些宏打印这些信息或者在LCD上显示程序的编译日期、时间,可以知道这个单片机上的固件是什么时候编译。帮助判断版本。

 ANSIC标准定义了可供C语言使用的预定义宏:
 __LINE__ : 在源代码中插入当前源代码行号
 __FILE__ : 在源代码中插入当前源代码文件名
 __DATE__ : 在源代码中插入当前编译日期
 __TIME__ : 在源代码中插入当前编译时间

其中标识符LINEFILE一般用来调试程序,打印信息,方便定位错误。

标识符DATETIME一般可以用来表示固件的版本,方便了解运行的程序是什么时候的版本。

标识符LINE是一个整数,其他的文件名称、日期、时间都是字符串。

二、打印示例

 printf("编译日期与时间: %s,%s\n", __DATE__,__TIME__);
 printf("当前所在行号:%d\r\n", __LINE__);
 printf("当前源文件名称:%s\r\n", __FILE__);
 printf("当前固件编译日期:%s\r\n", __DATE__);
 printf("当前固件编译时间:%s\r\n", __TIME__);

image-20220507175252472

三、C语言封装快捷Debug

 #define DEBUG
 ​
 #ifdef DEBUG
 static int DebugPrintf(const char *format, ...)
 {
     va_list arg_data;
     int     count;
     va_start(arg_data, format);                  /*  获取可变参数列表  */
     fflush(stdout);                              /*  强制刷新输出缓冲区  */
     count = vfprintf(stderr, format, arg_data);  /*  将信息输出到标准出错流设备  */
     va_end(arg_data);                            /*  可变参数列表结束  */
     return count;
 }
 #else
 static inline int DebugPrintf(const char *format, ...)
 {
 ​
 }
 #endif

通过DEBUG这个宏来开启是否开启调试信息打印功能,如果程序稳定后,不需要打印调试信息,就可以将DEBUG的定义取消掉即可。

完整代码:

 #include <stdio.h>
 #include <time.h>
 #include <stdlib.h>
 #include <string.h>
 #include <windows.h>
 #include <iostream>  
 #include <string> 
 ​
 using namespace std;
 ​
 //#define DEBUG
 ​
 #ifdef DEBUG
 static int DebugPrintf(const char *format, ...)
 {
     va_list arg_data;
     int     count;
     va_start(arg_data, format);                  /*  获取可变参数列表  */
     fflush(stdout);                              /*  强制刷新输出缓冲区  */
     count = vfprintf(stderr, format, arg_data);  /*  将信息输出到标准出错流设备  */
     va_end(arg_data);                            /*  可变参数列表结束  */
     return count;
 }
 #else
 static inline int DebugPrintf(const char *format, ...)
 {
     return 0;
 }
 #endif
 ​
 ​
 int main()
 {
     DebugPrintf("编译日期与时间: %s,%s\n", __DATE__,__TIME__);
     DebugPrintf("当前所在行号:%d\r\n", __LINE__);
     DebugPrintf("当前源文件名称:%s\r\n", __FILE__);
     DebugPrintf("当前固件编译日期:%s\r\n", __DATE__);
     DebugPrintf("当前固件编译时间:%s\r\n", __TIME__);
     return 0;
 }

四、STM32单片机上封装printf函数

 /*
 函数功能: 字符串发送
 */
 void USARTx_StringSend(USART_TypeDef *USARTx,u8 *str)
 {
    while(*str!='\0')
    {
        USARTx->DR=*str++;
        while(!(USARTx->SR&1<<7)){}
    }
 }
 ​
 //printf函数底层函数接口
 int fputc(int c, FILE* stream)
 {
     USART1->DR=c;
     while(!(USART1->SR&1<<7)){}
     return c;
 }
 ​
 /*
 函数功能: 格式化打印函数
 */
 char USART1_PRINTF_BUFF[1024];
 void USART1_Printf(char *fmt,...)
 {
    va_list ap;
    /*1. 初始化形参列表*/
    va_start(ap,fmt);
    /*2. 提取可变形参数据*/
     vsprintf(USART1_PRINTF_BUFF,fmt,ap);
    /*3. 结束,释放空间*/
     va_end(ap);
    /*4. 输出数据到串口1*/
    USARTx_StringSend(USART1,(u8*)USART1_PRINTF_BUFF);
    
    //USART1_Printf("%d%s",123,454656); 
    //int data=va_arg(ap,int);
 }

USART1_Printf的用法与printf是一样的,通过这个函数就可以实现数据打印到任意地方,包括改成存储到SD卡上。