__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([email protected]) 周立发(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([email protected]) 周立发(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([email protected]) 周立发(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([email protected]) 周立发(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([email protected]) 周立发(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([email protected]) 周立发(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([email protected]) 周立发(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([email protected]) 周立发(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,则程序运行到了哪里出了问题就会一目了然了。
仅仅为了获取函数名,就在函数体中嵌入硬编码的字符串,这种方法单调乏味还易导致错误,不如看一下怎样使用新的C99特性,在程序运行时获取函数名吧。
对象反射库、调试工具及代码分析器,经常会需要在运行时访问函数的名称,直到不久前,唯一能完成此项任务并且可移植的方法,是手工在函数体内嵌入一个带有该函数名的硬编码字符串,不必说,这种方法非常单调无奇,并且容易导致错误。本文将要演示怎样使用新的C99特性,在运行时获取函数名。
那么怎样以编程的方式从当前运行的函数中得到函数名呢?
答案是:使用__FUNCTION__ 及相关宏。
引出问题
通常,在调试中最让人心烦的阶段,是不断地检查是否已调用了特定的函数。对此问题的解决方法,一般是添加一个cout或printf()——如果你使用C语言,如下所示:
void myfunc() { cout<<"myfunc()"<<endl; //其他代码 } |
static const char __func__[] = "function-name"; |
void myfunc() { cout<<"__FUNCTION__"<<endl; } |
void show_name(const char * name) { cout<<name<<endl; } void myfunc() { show_name(__FUNCTION__); //输出:myfunc } void foo() { show_name(__FUNCTION__); //输出:foo } |
void myfunc() { show_name(__FUNCDNAME__); //输出:?myfunc@@YAXXZ } |
void myfunc() { show_name(__FUNCSIG__); // void __cdecl myfunc(void) } struct S { void myfunc() const { show_name(__FUNCSIG__); //void __thiscall S::myfunc(void) const } }; |