当前位置:   article > 正文

使用printf增加文件名、函数名、行号的打印_printf打印函数名

printf打印函数名

【速览】

最终效果如下,快速查看代码实现可以往下翻至黄底字体

【正文】

在实际工程中,随着项目越来越大,调试信息的作用也越来越重要。尤其是调试多线程程序时,详细的调试信息能够帮助我们更加快速地定位问题,因为此时存在线程调度,单步调试的意义已经不大。

我们希望调试信息包含文件名、函数名、行号以及我们自行添加的信息,于是可以这样实现:

printf("["__FILE__"][Line: %d][%s]: error happened!\n", __LINE__, __func__);

其中,__FILE__, __LINE__, __func__都是C语言的预定义符号,他们都在头文件"stdio.h"中。

__FILE__: 当前源文件名,表示为字符串常量;

__LINE__: 当前行号,表示为十进制整型常量;

__func__: 当前函数名,表示为字符串常量。

这样已经实现了文件名、行号、函数名的打印。那么,我们真的要每次打印都放上这么长一段代码么,有没有更好的实现方式呢?其实我们可以尝试用宏的方式来避免一些重复性的输入,为了方便,甚至可以加一个宏定义,统一控制是否打印:

  1. #include <stdio.h>
  2. //调试打印开关
  3. #define __DEBUG
  4. #ifdef __DEBUG
  5. #define info(format, ...) printf("["__FILE__"][Line: %d][%s]: "format"\n", __LINE__, __func__, ##__VA_ARGS__)
  6. #else
  7. #define info(format, ...)
  8. #endif
  9. int main()
  10. {
  11. info("error happened!");
  12. return 0;
  13. }

嗯,到这里好像差不多了,但是想想,我还能再进一步,我希望我的程序能够像linux终端那样输出五颜六色的东西,我用颜色区分打印信息的重要程度,红色表示错误、黄色表示警告、绿色表示正常信息,这样,就不用大海捞针一般地一行一行分析了。

  1. #include <stdio.h>
  2. //调试打印开关
  3. #define __DEBUG
  4. #ifdef __DEBUG
  5. #define normal_info(format, ...) printf("["__FILE__"][Line: %d][%s]: \033[32m"format"\033[32;0m\n", __LINE__, __func__, ##__VA_ARGS__)
  6. #define warning_info(format, ...) printf("["__FILE__"][Line: %d][%s]: \033[33m"format"\033[32;0m\n", __LINE__, __func__, ##__VA_ARGS__)
  7. #define error_info(format, ...) printf("["__FILE__"][Line: %d][%s]: \033[31m"format"\033[32;0m\n", __LINE__, __func__, ##__VA_ARGS__)
  8. #else
  9. #define normal_info(format, ...)
  10. #define warn_info(format, ...)
  11. #define error_info(format, ...)
  12. #endif
  13. int main(void)
  14. {
  15. normal_info("green");
  16. warning_info("yellow");
  17. error_info("red");
  18. return 0;
  19. }

__VA_ARGS__是C99规范中新增的可变参数的宏,如果可变参数被忽略或者为空,也就是只让printf打印字符串而不打印变量,则##操作将使预处理器去除它前面的逗号。

使printf打印带颜色的语法为:

printf("\033[字背景颜色;字体颜色m字符串\033[0m" );

"\033"表示对颜色的调用,与"\e"等价,这里我的背景色没有设置,使用的是默认值,字体颜色31、32、33分别表示红、绿、黄,打印完成之后将字体颜色设置为0,即恢复默认值,防止影响后面的打印颜色。

看一看打印效果吧

咦,Windows terminal上的颜色好像不大对劲,换Windows上的控制台看看

 这回颜色正了。

其实"stdio.h"中还有一些其他的预定义符号,比如日期、时间等,我们想要也可以加上。


2023.12.28更新

之前碰到过在Windows控制台(MS-DOS)使用颜色转义符不生效的问题,最近在一个项目中找到了原因并解决。我们写如下一行代码:

printf("\033[32mGreen\033[0m\n");

本意是想让Windows控制台输出绿色的"Green"字符串,但实际效果是下面这个样子:

通过查阅在Microsoft文档发现,Windows的控制台默认是不支持光标移动、颜色/字体模式等ANSI转义序列的,在碰到这些转义序列时无法解析,所以颜色转义失效的问题并不是代码带来的,而是控制终端的问题,网上有如下两种解决办法:

1. 安装ANSICON;

2. 通过代码启用控制台虚拟终端序列。

方法1安装一个软件以启用Windows控制台的ANSI转义序列支持,这种方法可以解决问题,但是每换一台电脑需要重新安装一次,通用性不高。方法2参考微软官方文档,通过如下操作,调用Windows API启用当前终端的ANSI转义序列支持(需要包含Windows.h):

  1. #ifdef _WIN32
  2. HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
  3. DWORD dwMode = 0;
  4. if(!GetConsoleMode(hTerminal, &dwMode))
  5. {
  6. printf("error:%d\n", GetLastError());
  7. return 0;
  8. }
  9. dwMode |= 0x0004;
  10. if (!SetConsoleMode(hTerminal, dwMode))
  11. {
  12. printf("error:%d\n", GetLastError());
  13. return 0;
  14. }
  15. #endif

然后颜色转义序列就可以生效了。上述代码中"_Win32"可以用来区分是否是Windows平台,"0x0004"选项表示启用终端控制字符解析,详见SetConsoleMode 函数的传入值"ENABLE_VIRTUAL_TERMINAL_PROCESSING"。

需要注意的是,Windows控制台的转义操作命令是跟在ESC后面的,而不是033,于是,我们的printf代码变成了如下模样(x1b表示十六进制的1B,其实就是ESC的ASCII码):

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <windows.h>
  4. int main()
  5. {
  6. #ifdef _WIN32
  7. HANDLE hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
  8. DWORD dwMode = 0;
  9. if(!GetConsoleMode(hTerminal, &dwMode))
  10. {
  11. printf("error:%d\n", GetLastError());
  12. return 0;
  13. }
  14. dwMode |= 0x0004;
  15. if (!SetConsoleMode(hTerminal, dwMode))
  16. {
  17. printf("error:%d\n", GetLastError());
  18. return 0;
  19. }
  20. #endif
  21. printf("\x1b[32mGreen\x1b[0m\n");
  22. return 0;
  23. }

再配合前面的日期、时间、函数名、行号打印,就可以在Windows平台和Linux平台为程序实现高效的日志记打印能力了

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/64799
推荐阅读
相关标签
  

闽ICP备14008679号