一、实现调试信息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.增加输出到文件中

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

  

  

  

  

  

  

  

  

  

  

 

posted on 2020-10-21 15:34  学习记录园  阅读(242)  评论(2编辑  收藏  举报