在标准C以及各中编译器中定义了一些对象宏, 这些宏的名称以"__"开头和结尾, 并且都是大写字符. 这些预定义宏可以被#undef, 也可以被重定义。
在ANSI C标准中定义了__FILE__,__LINE__,__DATA__,__TIME__,__STDC__等标准的预定义宏。GCC对其进行扩展,也定义了多个预定义宏。
概括起来GCC中可使用的预定义宏涵盖了如下几方面的信息:
1、宿主的信息:GNU的版本,编译器的版本,类型的相关信息,字节序信息等。
2、编译动作的信息:编译的日期、时间;编译时是否进行了时间或空间上的优化;定义的inline是否被编译器执行等。
3、文件的信息:文件名称、函数名称、行数信息、文件最后修改时间等等。
4、计数信息:__COUNTER__,__INCLUDE_LEVEL__等。
下面是一些常见的预定义宏的使用方法。
1、__FILE__,__LINE__,FUNCTION__
这是最常用到的预定义宏的组合,表示文件名、行数和函数名,用于程序运行期异常的跟踪。如:
- //-------file main.c----------
- #include <stdio.h>
- #include "myassert.h"
- int func(const char *filename);
- int main(int argc,char **argv)
- {
- MyAssert("two args are needed",argc==2);
- func(argv[1]);
- return 0;
- }
- //-------file func.c----------
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include "myassert.h"
- int func(const char *filename)
- {
- int fd;
- MyAssert("filename can not be null",filename);
- MyAssert("file not exist",0==access(filename,F_OK));
- fd = open(filename,O_RDONLY);
- close(fd);
- return 0;
- }
- //-------file myassert.h----------
- #ifndef __MY_ASSERT_H__
- #define __MY_ASSERT_H__
- #include <stdio.h>
- #include <stdlib.h>
- #define MyAssert(message,assertion) do{/
- if(!(assertion)){/
- printf("line %d in %s(%s)", __LINE__, __FILE__,__FUNCTION__);/
- if(message){/
- printf(" : %s",message);/
- }/
- printf("/n");/
- abort();/
- }/
- }while(0);
- #endif
- #Makefile
- TARGET = test
- CC = gcc
- CCFLAGS = -Wall
- OBJS = main.o func.o
- $(TARGET) : $(OBJS)
- $(CC) -o $@ $(OBJS) $(CCFLAGS)
- %.o : %.c
- $(CC) -o $@ -c $< $(CCFLAGS)
- clean:
- rm -rf *.o
- rm -rf $(TARGET)
运行./tset时:
line 9 in main.c(main) : two argvs are needed
Aborted
运行./test kkk时:
line 12 in func.c(func) : file not exist
Aborted
运行./test test时成功。
可见通过使用__FILE__,__LINE__,FUNCTION__宏,可以帮助我们精确的定位出现异常的文件、函数和行数。
2、__BASE_FILE__
这个宏是和__FILE__相对应的,表示主输入文件的名字,对于源文件而言__FILE__和__BASE_FILE__是一样的;对于头文件二者才可能不同。比如在上个例子中,__LINE__这个宏是在myassert.h文件中定义的,被main.c和func.c包含之后__FILE__的值
分别变成了main.c和func.c。但是当我们希望知道MyAssert这个宏具体实在哪个文件(实际上是myassert.h)中定义的话,就需要用到__BASE_FILE__。
下面的例子可以帮助加深理解:
- //-------file main.c----------
- #include <stdio.h>
- #include "basefile.h"
- int main(int argc, char *argv[])
- {
- printf("%s/n",sfile);
- printf("%s/n",hfile);
- return 0;
- }
- //-------file basefile.h----------
- const char sfile[]= __FILE__;
- const char hfile[]= __BASE_FILE__;
gcc main.c &&./a.out 得到:
basefile.h
main.c
3、__DATE__,__TIME__
用于得到最后一次编译的日期和时间(字符串形式):
- #include <stdio.h>
- //-------file main.c----------
- int main()
- {
- printf("DATE : %s/n",__DATE__);
- printf("TIME : %s/n",__TIME__);
- }
gcc main.c &&./a.out 得到:
DATE : Jan 27 2011
TIME : 17:12:55
4、__TIMESTAMP__
和__TIME__的格式相同。同于得到本文件最后一次被修改的时间。
5、__GNUC__、__GNUC_MINOR__、__GNUC_MINOR__、__GNUC_PATCHLEVEL__
用于得到GNU版本:
- #include <stdio.h>
- int main()
- {
- if( __GNUC__ > 4 ||
- (__GNUC__ == 4 && (__GNUC_MINOR__ > 2 ||
- (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ > 0)))){
- printf("GNUC version is later than 3.3.2/n");
- }else{
- printf("GNUC version is older than 3.3.2/n");
- }
- }
6、__VERSION__
用于得到编译器的版本
- //-------file main.c----------
- #include <stdio.h>
- int main()
- {
- printf("Version : %s/n",__VERSION__);
- return 0;
- }
gcc main.c && ./a.out得到:
Version : 4.1.2 (Gentoo 4.1.2 p1.0.2)
可以和gcc -v相互验证
7、__COUNTER__
自身计数器,用于记录以前编译过程中出现的__COUNTER__的次数,从0开始计数。常用于构造一系列的变量名称,函数名称等。如:
- //-------file main.c----------
- #include <stdio.h>
- #define FUNC2(x,y) x##y
- #define FUNC1(x,y) FUNC2(x,y)
- #define FUNC(x) FUNC1(x,__COUNTER__)
- int FUNC(var);
- int FUNC(var);
- int main() {
- var0 = 0;
- var1 = 1;
- printf("%d/n",var0);
- printf("%d/n",var1);
- return 0;
- }
gcc main.c &&a.out得到结果:
0
1
这里使用__COUNTER__构造了两个变量:var0,var1。
8、__INCLUDE_LEVEL__
用于表示文件被包含的计数,从0开始递增,常作为递归包含的限制条件。如:
- //-------file main.c----------
- #include <stdio.h>
- int main()
- {
- #define REP_LIMIT 10
- #define REP(BLAH) printf("%d ", BLAH);
- #include "rep.h"
- printf("/n");
- return 0;
- }
- //--------file rep.h----------
- #if __INCLUDE_LEVEL__ < REP_LIMIT
- REP(__INCLUDE_LEVEL__)
- #include "rep.h"
- #endif
gcc main.c && ./a.out,得到结果:
1 2 3 4 5 6 7 8 9
在这个例子中文件rep.h自包含了9次,执行了9次REP(BLAH)。
实际上,__INCLUDE_LEVEL__最多的是和#include __FILE__组合使用,用于表示一个递归。如:
- //-------file main.c----------
- #ifndef AUTOINC
- #define AUTOINC
- #include <stdio.h>
- #define MAX_LEVEL 10
- int main()
- {
- int i = 0;
- #include __FILE__
- printf("/n");
- return 0;
- }
- #undef AUTOINC
- #endif
- #ifdef AUTOINC
- #if __INCLUDE_LEVEL__ <= MAX_LEVEL
- printf("%d ",__INCLUDE_LEVEL__);
- #include __FILE__
- #if __INCLUDE_LEVEL__ != MAX_LEVEL
- printf("%d ",__INCLUDE_LEVEL__);
- #endif
- #endif
- #endif
gcc main.c && ./a.out得到结果:
1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1