一、实现调试信息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传递进来的内容以字符串的形式输出
#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:加入标记开关
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 条件片段需要以此为结尾
举例:
#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.增加输出到文件中