一、实现调试信息log日志打印功能
调试信息 第一步需要定位程序中的问题。
二 预处理命令 宏定义 以#开头 功能字符串替换 主要作用在预处理阶段
c源代码-----》(预处理阶段)待编译源码----》(编译阶段)目标文件----》(链接)可执行程序
可执行程序的功能由第二步的待编译源码决定的 而不是c源代码
************************g++ -E main.cpp 查看待编译源代码的命令 可以重定向到文件便于查看***************
定义符号常量
#define pi 3.1415926
定义傻瓜表达式
#define MAX(a,b) (a)>(b)?(a):(b)
定义代码段
#define p(a){printf("%d\n",a)} 宏定义需要写在一行之内,如果不能显示下 需要 “\” 连接符号
定义有参数的宏
#define S(a,b) a*b 宏定义
printf("S(3,4)=%d\n",s(3,4)); 替换为3*4
printf("S(int,p)=%d\n",s(3,4)); 替换为 int *p 在这里是通过宏的方式定义指针变量。
S(int,p)=&n; 相当于 int *p=&n 定义指针
以上为基础宏定义
预处理命令 include 头文件中的内容 进入到待编译文件中展开 所以源文件中包含的头文件越多,预编译生成的待编译文件内容越多。
三:预处理命令---预定义的宏 编译器的内置宏
__DATE__ 日期
__TIME__ 时间
__LINE__ 行号
—FILE__ 文件名
__func__ 函数名
—PRETTY_FUNCTION 更详细的函数信息
printf("__FILE__=%S\n",__file__); 输出:main.cpp
printf("__LINE__=%d\n",__LINE__); 输出:30 当前行号
四:举例 LOG宏 实现日志打印功能
1.args...
gcc编译器中的cpp预编译器 支持args...
第一个参数传递给frm 第二个开始传递给args...
2.##args 将args...中的多个参数 串联起来 ...代表变参 #的作用为连接操作 如果下面的printf(frm,##args) 中的##去掉 则编译不通过。
3.#frm 将frm传递进来的内容以字符串的形式输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <iostream> #define LOG(frm,args...){\ printf ( "[%s:%s:%d]" ,__FILE__,__func__,__LINE__);\ printf (frm,##args);\ printf ( "\n" );\ } using namespace std; void test(){ LOG( "hello world" ); return ; } int main() { int a=123; LOG( "a=%d=%d" ,a,2*a); LOG( "hello world" ); test(); return 0; } |
举例:
例子1
#define DBG_OUTPUT(fmt,args...) printf("CK File[%s:%s(%d)]:" fmt "\n", __FILE__,__FUNCTION__, __LINE__, ##args) //宏定义
DBG_OUTPUT("j[%d]k[%d]" , j,k);
//实际调用
实际输出:CK File ** j[-15]k[241]
例子2
#define DBG_OUTPUT(fmt,args...) printf("CK File[%s:%s(%d)]:" #fmt "\n", __FILE__,__FUNCTION__, __LINE__, ##args)
DBG_OUTPUT(j[%d]k[%d], j, k);
gcc的option -E 为向标准输入输出中,输出预编译结果。
gcc -E test.c >& test.i 生成编译结果 保存在test.i的文本文件中。 查看DBG_OUTPUT 语句的位置 看其被编译替换成什么
两个参数连接时,可以允许参数的值为空。 这就是##的作用
上例中的 ##args,输入的参数可以为空;如果是args 输入的实参不能为空。
举例:
#define CAT(a,b) a##b
hz=1000;
CAT(h,z)=500; //实际的结果是hz=500;
printf("%d\n",hz);//输出的结果为500
如果不清楚 ,需要看待编译源代码:g++ -E main.cpp>>temp.cpp 重定向到temp.cpp 查看temp.cpp文件
两个##的作用是允许前后两个参数为空。
五:开发版和上线版的区别
开发需要有调试信息
上线不能有调试信息
不改变代码的情况,需要使用预编译宏的方式进行总开关操作,来实现调试信息的显示和关闭。
方法1:加入标记开关
1 2 3 4 5 6 7 | int log_flag=0; //开关按钮 0 和1 #define LOG(frm,args...) do {\ //跳出局部作用域 从循环内部跳出 if (log_flag==0) break ; printf ( "[%s:%s:%d]" ,__FILE__,__func__,__LINE__);\ printf (frm,##args);\ printf ( "\n" );\ } while (0); |
需要打开则使得log_flag=1即可。
方法2:
预处理命令--条件式编译 主要在做代码剪裁。
#ifdef DEBUG 是否定义了DEBUG宏
#ifdef DEBUG 是否定义了DEBUG宏
#if MAX_N==5 宏MAX_N是否等于5
#elif MAX_N==4 否则宏MAX_N是否等于4
#else
#endif 条件片段需要以此为结尾
举例:
1 2 3 4 5 6 7 8 9 | #ifdef DEBUG 如果定义DEBUG宏 代表需要调试程序 则执行下面的代码 #define LOG(frm,args...){\ printf ( "[%s:%s:%d]" ,__FILE__,__func__,__LINE__);\ printf (frm,##args);\ printf ( "\n" );\ } #else 不调试程序信息 则执行下面的空代码 #define LOG(frm,args...) #endif |
如果在最前面,加入#define DEBUG 则显示调试信息 否则不显示
编译命令中 加入DEBUG宏:g++ -DDEBUG log.cpp
3.修改makefile文件
all:
g++ main.cpp
debug:
g++ -DDEBUG main.cpp
在shell命令行中:
make 就是执行all中的命令 相当于上线版
make debug 就是执行带调试的命令。 相当于开发版
4.调试信息提示 LOG等级信息
等级信息
info error debug warning fatal
关闭某一个等级下的日志信息。
5.日志输出变为多线程
6.增加输出到文件中
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!