__FILE__,__LINE__,__DATE__,__TIME__, __FUNCTION__

C++有四个常用的预定义名字,分别为:__FILE__,__LINE__,__DATE__,__TIME__

__FILE__:记录文件的路径加名称

__LINE__:记录文件已经被编译的行数

__DATE__:记录文件的编译日期

__TIME__:记录文件的编译时间

可以当作变量直接使用,一般用作程序调试

例子:

#include <iostream>

using namespace std;

int main()
{
 
    cout << "File = " << __FILE__ << '\n'
       << "LINE = " << __LINE__ << '\n'
       << "DATE = " << __DATE__ << '\n'
       << "TIME = " << __TIME__
       << endl; 
    getchar();
}

运行结果:

File = G:/program/study/c++/test1.cpp
LINE = 17
DATE = May 27 2004
TIME = 09:59:01

利用__FILE__,__LINE__,FUNCTION__实现代码跟踪调试

linux下c语言编程 )先看下简单的初始代码:注意其编译运行后的结果。

root@xuanfei-desktop:~/cpropram/2# cat global.h //头文件
#ifndef CLOBAL_H
        #define GLOBAL_H
        #include <stdio.h>
        int funca(void);
        int funcb(void);
#endif
root@xuanfei-desktop:~/cpropram/2# cat funca.c //函数a
#include "global.h"
int funca(void)
{
printf ("this is function\n");
return 0;
}
root@xuanfei-desktop:~/cpropram/2# cat funcb.c //函数b
#include "global.h"
int funcb(void)
{
printf ("this is function\n");
return 0;
}
root@xuanfei-desktop:~/cpropram/2# gcc -Wall funca.c funcb.c main.c //联合编译
root@xuanfei-desktop:~/cpropram/2# ./a.out //运行
this is main
this is function
this is main
this is function
this is main

相同结果很难让人看出那里出错,下面我们用用 __FILE__,__LINE__,__FUNCTION__加入代码,看看有什么区别吗.
把 __FILE__,__LINE__,__FUNCTION__加入到mail.c中
root@xuanfei-desktop:~/cpropram/2# cat main.c
#include "global.h"
int main(int argc, char **argv)
{
    printf("%s(%d)-%s: this is main\n",__FILE__,__LINE__,__FUNCTION__);
    funca();
    printf("%s(%d)-%s: this is main\n",__FILE__,__LINE__,__FUNCTION__);
    funcb();
    printf("%s(%d)-%s: this is main\n",__FILE__,__LINE__,__FUNCTION__);
    return 0;
}
root@xuanfei-desktop:~/cpropram/2# gcc -Wall funca.c funcb.c main.c
root@xuanfei-desktop:~/cpropram/2# ./a.out
main.c(4)-main: this is main
this is function
main.c(6)-main: this is main
this is function
main.c(8)-main: this is main

上面的结果main.c(4)-main:this is main 表示在mian.c源代码的第四行main函数里边打印出来的 this is main
那样的话就很方便的让程序员对自己的程序进行排错!
为了更方便的使用它我们可以通过在global.h代码中进行宏定义
root@xuanfei-desktop:~/cpropram/2# cat global.h
#ifndef CLOBAL_H
        #define GLOBAL_H
        #include <stdio.h>
        int funca(void);
        int funcb(void);
        #define DEBUGFMT  "%s(%d)-%s"
        #define DEBUGARGS __FILE__,__LINE__,__FUNCTION__
#endif
root@xuanfei-desktop:~/cpropram/2# cat funca.c
#include "global.h"
int funca(void)
{
printf (DEBUGFMT " this is function\n",DEBUGARGS);
return 0;
}
root@xuanfei-desktop:~/cpropram/2# cat funcb.c
#include "global.h"
int funcb(void)
{
printf (DEBUGFMT " this is function\n",DEBUGARGS);
return 0;
}
root@xuanfei-desktop:~/cpropram/2# cat main.c
#include "global.h"
int main(int argc, char **argv)
{
    printf(DEBUGFMT "this is main\n", DEBUGARGS);
    funca();
    printf(DEBUGFMT "this is main\n", DEBUGARGS);
    funcb();
    printf(DEBUGFMT "this is main\n", DEBUGARGS);
    return 0;
}
root@xuanfei-desktop:~/cpropram/2# gcc -Wall funca.c funcb.c main.c
root@xuanfei-desktop:~/cpropram/2# ./a.out
main.c(4)-mainthis is main
funca.c(4)-funca this is function
main.c(6)-mainthis is main
funcb.c(4)-funcb this is function
main.c(8)-mainthis is main
root@xuanfei-desktop:~/cpropram/2#

这就是通过定义__FILE__,__LINE__,FUNCTION__的宏来简单实现代码的跟踪调试:)

下面是一个可供调试用的头文件
#ifndef _GOLD_DEBUG_H
#define _GOLD_DEBUG_H

 

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */

//#define GI_DEBUG

#ifdef GI_DEBUG

#define GI_DEBUG_POINT()   printf("\n\n[File:%s Line:%d] Fun:%s\n\n", __FILE__, __LINE__, __FUNCTION__)
#define dbg_printf(arg...)   printf(arg);

#define GI_ASSERT(expr)                                     \
    do{                                                     \
        if (!(expr)) { \
            printf("\nASSERT failed at:\n  >File name: %s\n  >Function : %s\n  >Line No. : %d\n  >Condition: %s\n", \
                    __FILE__,__FUNCTION__, __LINE__, #expr);\
        } \
    }while(0);

/*调试宏, 用于暂停*/
#define GI_DEBUG_PAUSE()           \
 do               \
 {               \
  GI_DEBUG_POINT();          \
  printf("pause for debug, press 'q' to exit!\n");  \
  char c;             \
  while( ( c = getchar() ) )        \
   {             \
    if('q' == c)         \
     {           \
      getchar();        \
      break;         \
     }           \
   }             \
 }while(0);
#define GI_DEBUG_PAUSE_ARG(arg...)          \
  do               \
  {               \
   printf(arg);           \
   GI_DEBUG_PAUSE()          \
  }while(0);


#define GI_DEBUG_ASSERT(expression)      \
if(!(expression))                        \
{                                  \
    printf("[ASSERT],%s,%s:%d\n", __FILE__,  __FUNCTION__, __LINE__);\
    exit(-1);             \
}
#else
#define GI_ASSERT(expr)
#define GI_DEBUG_PAUSE()
#define GI_DEBUG_PAUSE_ARG(arg...)
#define GI_DEBUG_POINT()
#define dbg_printf(arg...)
#define GI_DEBUG_ASSERT(expression)

#endif

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */


#endif

 

       作为一个Linux系统下的C程序员,你可能发现调试程序是个比较麻烦的工作,虽然已经有gdb,kgdb等专业的调试软件,但如果对这些软件运用不熟练是根本达不到调试程序找出bug的目的的。又或者你对gdb已经很熟了,但运行gdb开始调试后在哪里设置断点成了你头痛的问题?当然,你可以从程序开始就以单步运行step by step来调试程序,但这会耗去你很多时间。
如果你能很好地跟踪并记录程序的运行情况,那么一切将变得简单。下面我以一个实例说明我是如何操作的:
首先我有一个程序主体main,其代码如下:
//trace.c 开始///
/*********************************************************************
*filename: trace.c
*purpose: demonstrate how to trace program easily. We'll
*         get every source filename, function-name, and the
*         current line number wrote into a stream. The stream
*         can be a file descriptor or stdout
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*date time:2005-11-30 00:30
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/

#include "global.h"
#include "MyFuncOne.h"
#include "MyFuncTwo.h"

int x;

int main(int argc, char ** argv)
{
    x = 5;
    dump(stdout, "now: x=%d", x);
    MyFuncOne();
    MyFuncTwo();
    dump(stdout, "now: x=%d", x);
    return 0;
}
//trace.c 结束///

这个main里面引用了global.h,MyFuncOne.h,MyFuncTwo.h等,global.h里面是一个宏定义,定义了dump宏,其内容如下:
//global.h 开始///
#ifndef GLOBAL_H
    #define GLOBAL_H
    #include "dump.h"

/*********************************************************************
*filename: global.h
*purpose: dump function declare
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*date time:2005-11-30 00:30
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/
    #ifdef DEBUG
        #define dump(fp, x...)  debug_print(fp, __FILE__, __LINE__, __FUNCTION__, ##x);
    #else
        #define dump(fp, x...)
    #endif

#endif
//global.h 结束///
global.h这里又引用了dump.h,其内容稍后再贴出来。
MyFuncOne.h和MyFuncTwo.h分别定义了两个函数MyFuncOne和MyFuncTwo,其内容如下:
//MyFuncOne.h 开始///
#ifndef MYFUNC_ONE_H
    #define MYFUNC_ONE_H
    #include "global.h"
/*********************************************************************
*filename: MyFuncOne.h
*purpose: MyFuncOne function declare
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*date time:2005-11-30 00:30
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/
    void MyFuncOne();
#endif
//MyFuncOne.h 结束///

//MyFuncTwo.h 开始///
#ifndef MYFUNC_TWO_H
    #define MYFUNC_TWO_H
    #include "global.h"
/*********************************************************************
*filename: MyFuncTwo.h
*purpose: MyFuncTwo function declare
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/
    void MyFuncTwo();
#endif
//MyFuncTwo.h 结束///

//MyFuncOne.c 开始///
#include "MyFuncOne.h"

extern int x;
/*********************************************************************
*filename: MyFuncOne.c
*purpose: MyFuncOne function instance
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*date time:2005-11-30 00:30
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/
void MyFuncOne()
{
    x *= -2;
    dump(stdout, "MyFuncOne, now: x=%d", x);
}
//MyFuncOne.c 结束///

//MyFuncTwo.c 开始///
#include "MyFuncTwo.h"

extern int x;
/*********************************************************************
*filename: MyFuncTwo.h
*purpose: MyFuncOne function declare
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*date time:2005-11-30 00:30
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/
void MyFuncTwo()
{
    x++;
    dump(stdout, "MyFuncTwo, now: x=%d", x);
}
//MyFuncTwo.c 结束///

现在该是时候说dump了,先看看其实现:
//dump.h 开始///
#ifndef DUMP_H
    #define DUMP_H
    #include <sys/param.h>
    #include <stdio.h>
    #include <stdarg.h>
    #include <time.h>
/*********************************************************************
*filename: dump.h
*purpose: debug_print function declare
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*date time:2005-11-30 00:30
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/
    void debug_print(FILE * fp, const char * filename, const int line, const char * funcname, char *fmt, ...);
#endif
//dump.h 结束///

//dump.c 开始///
#include "dump.h"

/*********************************************************************
*filename: dump.c
*purpose: debug_print function instance
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
*date time:2005-11-30 00:30
*Note: 任何人可以任意复制代码并运用这些代码,当然包括你的商业用途
*                         但请遵循GPL        
*********************************************************************/
void debug_print(FILE * fp, const char * filename, const int line, const char * funcname, char *fmt, ...)
{
    char buf[1024];
    time_t t;
    struct tm * now;
    va_list ap;

    time(&t);
    now = localtime(&t);
    va_start(ap, fmt);
    fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d -- %s(%d):%s DEBUG:@\"", now -> tm_year + 1900, now -> tm_mon + 1, now -> tm_mday, now -> tm_hour, now -> tm_min, now -> tm_sec, filename, line, funcname);
    vsprintf(buf, fmt, ap);
    fprintf(fp, "%s\"@\n", buf);
    va_end(ap);
}
//dump.c 结束///

大家一定注意到:这个程序和大家一般写的程序并无大的区别,除了__FILE__, __LINE__, __FUNCTION__等几个宏和dump宏,__FILE__, __LINE__, __FUNCTION__是编译的时候已经内置了的几个宏,用来表明当前程序运行到了哪个源文件的哪一行,同时表明当前在哪个函数里面。而我们定义一个dump宏就是用来把这些信息送到一个文件句柄去。比如你的日志文件。这样,在任何程序需要的地方都可以加上一句dump来把需要的调试信息记录下来。
比如编译上述程序:
gcc -DDEBUG trace.c dump.c MyFuncOne.c MyFuncTwo.c -o trace
然后运行程序可以得到如下结果:
2005-11-30 00:40:38 -- trace.c(22):main DEBUG:@"now: x=5"@
2005-11-30 00:40:38 -- MyFuncOne.c(15):MyFuncOne DEBUG:@"MyFuncOne, now: x=-10"@
2005-11-30 00:40:38 -- MyFuncTwo.c(15):MyFuncTwo DEBUG:@"MyFuncTwo, now: x=-9"@
2005-11-30 00:40:38 -- trace.c(25):main DEBUG:@"now: x=-9"@
第一行:显示在trace.c源文件的第22行处,即main函数内打印出:now: x=5;
第二行:显示在MyFuncOne.c源文件的第15行处,即MyFuncOne函数内打印出:MyFuncOne, now: x=-10;
第三行:显示在MyFuncTwo.c源文件的第15行处,即MyFuncTwo函数内打印出:MyFuncTwo, now: x=-9;
第四行:显示在trace.c源文件的第25行处,即main函数内打印出:now: x=-9;

如果程序加多点dump,则程序运行到了哪里出了问题就会一目了然了。

在C语言中以编程的方式获取函数名

仅仅为了获取函数名,就在函数体中嵌入硬编码的字符串,这种方法单调乏味还易导致错误,不如看一下怎样使用新的C99特性,在程序运行时获取函数名吧。

  对象反射库、调试工具及代码分析器,经常会需要在运行时访问函数的名称,直到不久前,唯一能完成此项任务并且可移植的方法,是手工在函数体内嵌入一个带有该函数名的硬编码字符串,不必说,这种方法非常单调无奇,并且容易导致错误。本文将要演示怎样使用新的C99特性,在运行时获取函数名。

  那么怎样以编程的方式从当前运行的函数中得到函数名呢?

  答案是:使用__FUNCTION__ 及相关宏。

  引出问题

  通常,在调试中最让人心烦的阶段,是不断地检查是否已调用了特定的函数。对此问题的解决方法,一般是添加一个cout或printf()——如果你使用C语言,如下所示:

void myfunc()
{
cout<<"myfunc()"<<endl;
//其他代码
}

  通常在一个典型的工程中,会包含有数千个函数,要在每个函数中都加入一条这样的输出语句,无疑难过上“蜀山”啊,因此,需要有一种机制,可以自动地完成这项操作。

   获取函数名

  作为一个C++程序员,可能经常遇到 __TIME__、__FILE__、__DATE__ 这样的宏,它们会在编译时,分别转换为包含编译时间、处理的转换单元名称及当前时间的字符串。

  在最新的ISO C标准中,如大家所知的C99,加入了另一个有用的、类似宏的表达式__func__,其会报告未修饰过的(也就是未裁剪过的)、正在被访问的函数名。请注意,__func__不是一个宏,因为预处理器对此函数一无所知;相反,它是作为一个隐式声明的常量字符数组实现的:

static const char __func__[] = "function-name";

  在function-name处,为实际的函数名。为激活此特性,某些编译器需要使用特定的编译标志,请查看相应的编译器文档,以获取具体的资料。

  有了它,我们可免去大多数通过手工修改,来显示函数名的苦差事,以上的例子可如下所示进行重写:

void myfunc()
{
cout<<"__FUNCTION__"<<endl;
}

  官方C99标准为此目的定义的__func__标识符,确实值得大家关注,然而,ISO C++却不完全支持所有的C99扩展,因此,大多数的编译器提供商都使用 __FUNCTION__ 取而代之,而 __FUNCTION__ 通常是一个定义为 __func__ 的宏,之所以使用这个名字,是因为它已受到了大多数的广泛支持。

  在Visual Studio 2005中,默认情况下,此特性是激活的,但不能与/EP和/P编译选项同时使用。请注意在IDE环境中,不能识别__func__ ,而要用__FUNCTION__ 代替。

  Comeau的用户也应使用 __FUNCTION__ ,而不是 __func__ 。

  C++ BuilderX的用户则应使用稍稍不同的名字:__FUNC__ 。

  GCC 3.0及更高的版本同时支持 __func__ 和__FUNCTION__ 。

  一旦可自动获取当前函数名,你可以定义一个如下所示显示任何函数名的函数:

void show_name(const char * name)
{
cout<<name<<endl;
}

void myfunc()
{
show_name(__FUNCTION__); //输出:myfunc
}

void foo()
{
show_name(__FUNCTION__); //输出:foo
}

  因为 __FUNCTION__ 会在函数大括号开始之后就立即初始化,所以,foo()及myfunc()函数可在参数列表中安全地使用它,而不用担心重载。

   签名与修饰名

  __FUNCTION__ 特性最初是为C语言设计的,然而,C++程序员也会经常需要有关他们函数的额外信息,在Visual Studio 2005中,还支持另外两种非标准的扩展特性:__FUNCDNAME__ 与 __FUNCSIG__ ,其分别转译为一个函数的修饰名与签名。函数的修饰名非常有用,例如,在你想要检查两个编译器是否共享同样的ABI时,就可派得上用场,另外,它还能帮助你破解那些含义模糊的链接错误,甚至还可用它从一个DLL中调用另一个用C++链接的函数。在下例中,show_name()报告了函数的修饰名:

void myfunc()
{
show_name(__FUNCDNAME__); //输出:?myfunc@@YAXXZ
}

  一个函数的签名由函数名、参数列表、返回类型、内含的命名空间组成。如果它是一个成员函数,它的类名和const/volatile限定符也将是签名的一部分。以下的代码演示了一个独立的函数与一个const成员函数签名间的不同之处,两个函数的名称、返回类型、参数完全相同:

void myfunc()
{
show_name(__FUNCSIG__); // void __cdecl myfunc(void)
}

struct S
{
void myfunc() const
{
show_name(__FUNCSIG__); //void __thiscall S::myfunc(void) const
}
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值