今天在看Linux系统编程这本书的代码的时候看到了__GNUC__,不太清楚这个宏所以去查了一下,以此记录。GNU C预定义了一系列的宏,这些宏都是以双下划线开始的,这里只讲一下__GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__,完整的GNU C的预定义宏可以到这里查看:
https://gcc.gnu.org/onlinedocs/gcc-5.1.0/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros
__GNUC__ 、__GNUC_MINOR__ 、__GNUC_PATCHLEVEL__分别代表gcc的主版本号,次版本号,修正版本号。__GNUC_PATCHLEVEL__
是从gcc 3.0以后才有的,在这之前的gcc是没有预定义这个宏的。我们可以用gcc --version来查看自己系统中的gcc版本,现在的gcc版本普遍都是3.0以后了吧,就我的系统而言,是4.9.2,那么对应的__GNUC__就是4,__GNUC_MINOR__就是9,__GNUC_PATCHLEVEL__就是2。这几个宏的类型都是int,被扩展后,会得到整数的字面值。由于是宏,因此我们可以通过只预处理源程序来观察他们的文本值。比如,只对下面这段代码进行预处理,预处理(gcc -E)以后是对宏进行直接的替换,所以我们就能看到这三个宏的文本值:
#include <stdio.h>
int main()
{
#ifdef __GNUC__
printf("__GNUC__ = %d\n",__GNUC__);
#endif
#ifdef __GNUC_MINOR__
printf("__GNUC_MINOR__ = %d\n",__GNUC_MINOR__);
#endif
#ifdef __GNUC_PATCHLEVEL__
printf("__GNUC_PATCHLEVEL__ = %d\n",__GNUC_PATCHLEVEL__);
#endif
return 0;
}
预编译以后的文件函数部分:
int main()
{
printf("__GNUC__ = %d\n",4);
printf("__GNUC_MINOR__ = %d\n",9);
printf("__GNUC_PATCHLEVEL__ = %d\n",2);
return 0;
}
这样就很直观地看到,__GNUC__被替换成了4,__GNUC_MINOR__被替换成了9,__GNUC_PATCHLEVEL__替换成了2。
为什么要预定义了这三个宏呢?这是为了方便我们在针对特定版本的gcc编译器进行代码编写的,比如我们的代码要求gcc的版本至少在3.2.0以上,我们就可以写成如下方式的条件编译:
/* Test for GCC > 3.2.0 */
#if __GNUC__ > 3 || \
(__GNUC__ == 3 && (__GNUC_MINOR__ > 2 || \
(__GNUC_MINOR__ == 2 && \
__GNUC_PATCHLEVEL__ > 0)))
printf("gcc > 3.2.0\n");
//...
#endif
注意上面把条件编译#if的条件写成了多行的时候(和宏定义一样,如果宏定义一行写不完,要在最后加一个行继续符'\'),每行最后的行继续符'\'后面不能跟任何符号,空格、制表符等都不行,他表示下一行的也是并列条件(通常为||或&&的右操作数),通常在编译以前会把行继续符'\'以及前面的换行符都去掉,这样就可以看作是同一行的了。
当然有的人觉得上面的条件那么大一串看起来非常不顺眼,理解起来也不容易,这时候我们可以自己定义一个宏GCC_VERSION用来表示gcc版本,原理也很简单就是把主版本号*10000+次版本号*100+修订版本号,最终用这个值来判断gcc的版本号:
#include <stdio.h>
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
int main()
{
/* Test for GCC > 3.2.0 */
#if GCC_VERSION > 30200
printf("gcc > 3.2.0\n");
//...
#endif
return 0;
}
好啦,对__GNUC__这个预定义的宏变量算是有了一个基本的了解,作用是用来针对特定版本的gcc进行编写代码,至于其他预定义的宏呢可以去本文刚开始的时候给出的网站上查看,他们各自的作用也都写的非常清楚。