gcc中预定义的宏__GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__

news/2025/3/29 6:54:27/

今天在看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进行编写代码,至于其他预定义的宏呢可以去本文刚开始的时候给出的网站上查看,他们各自的作用也都写的非常清楚。

一、预定义__GNUC__宏

1 __GNUC__ 是gcc编译器编译代码时预定义的一个宏。需要针对gcc编写代码时, 可以使用该宏进行条件编译。

2 __GNUC__ 的值表示gcc的版本。需要针对gcc特定版本编写代码时,也可以使用该宏进行条件编译。

3 __GNUC__ 的类型是“int”,该宏被扩展后, 得到的是整数字面值。可以通过仅预处理,查看宏扩展后的文本。

示例:

  #include <assert.h>

  #include <stdio.h>

  #include <typeinfo>

  #ifndef __GNUC__

    #error sample for gcc compiler

  #else

    /* use gcc special extension: #warning , __attribute__, etc. */

  #endif

  int main()

  {

    printf("hello gcc %d\n",__GNUC__);

    assert( typeid(__GNUC__)==typeid(int) );

    printf("press Enter to exit\n");

    (void)getchar();

  }

二、预定义_MSC_VER宏

1 _MSC_VER是微软C/C++编译器——cl.exe编译代码时预定义的一个宏。需要针对cl编写代码时, 可以使用该宏进行条件编译。

2 _MSC_VER的值表示cl的版本。需要针对cl特定版本编写代码时, 也可以使用该宏进行条件编译。

3 _MSC_VER的类型是"int"。该宏被扩展后,得到的是整数字面值。可以通过仅预处理, 查看宏扩展后的文本。

示例:

  /* _MSC_VER\_MSC_VER.cpp */

   #include <stdio.h>

  #include <stdlib.h>

  #include <typeinfo>

  #define TO_LITERAL(text) TO_LITERAL_(text)

  #define TO_LITERAL_(text) #text

  #ifndef _MSC_VER

    #error sample for msvc compiler

  #else

    /* use msvc special extension: #pragma message,__declspec,__stdcall,etc. */

    #pragma message("----------------------------------------\n")

    #pragma message("----------------------------------------\n")

    #pragma message("---------- hello msvc " TO_LITERAL(_MSC_VER) " -------------")

    #pragma message("\n----------------------------------------\n")

    #pragma message("----------------------------------------\n")

    extern __declspec(dllimport) void __stdcall declare_but_dont_reference(void);

   #endif

    

  int main()

  {

    printf("hello msvc, version=%d\n",_MSC_VER);

    printf("typeof _MSC_VER=\"%s\"\n",typeid(_MSC_VER).name());

    system("pause"); /* msvc only on windows? */

    return 0;

  }


http://www.ppmy.cn/news/19829.html

相关文章

四轮两驱小车(四):STM32驱动5路灰度传感器PID循迹

目录 前言&#xff1a; 小车效果展示&#xff1a; 5路数字灰度传感器&#xff1a; 巡线思路&#xff1a; 加入PID调节的代码&#xff1a; 前言&#xff1a; 之前买了一批5路灰度传感器&#xff0c;想用这传感器进行循迹&#xff0c;无奈网上和官方的资料提供的还是比较少&a…

Opencv项目实战:20 单手识别数字0到5

目录 0、项目介绍 1、效果展示 2、项目搭建 3、项目代码展示 HandTrackingModule.py Figures_counter.py 4、项目资源 5、项目总结 0、项目介绍 今天要做的是单手识别数字0到5&#xff0c;通过在窗口展示&#xff0c;实时的展示相应的图片以及文字。 在网上找了很久的…

C语言进阶——文件管理

每当我们写好一段代码运行结束之后&#xff0c;再次运行的时候就会发现&#xff0c;之前在终端上输入的数据都会消失&#xff0c;那么如何把之前输入的数据保存下来呢&#xff1f; 我们一般把数据持久化的方式有把数据存放在磁盘文件中、存放到数据库。打印等方式进行保存。 …

【二叉树】java实现代码,详解二叉树,带大家更深刻的掌握二叉树递归思想

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1fa90;&#x1fa90;&#x1fa90;&#xff0c;在探索数据结构的旅程中&#xff0c;二叉树可以说是数据结构中的重点&#xff0c;笔试面试经常出现的问题&#xff0c;同时也是难点。&#x1f425;&#x1f425;&#x1f4…

【数据结构之二叉树系列】万字深剖普通二叉树的遍历+分治算法思想

目录前言一、背景知识二、前序遍历三、中序遍历四、后序遍历五、求二叉树中结点的个数1. 遍历计数&#xff08;1&#xff09;前序遍历计数&#xff08;2&#xff09;中序遍历计数&#xff08;3&#xff09;后序遍历计数2.分治算法思想&#xff08;推荐&#xff09;敬请期待前言…

芒果改进YOLOv7系列:超越ConvNeXt结构,原创结合Conv2Former改进结构,Transformer 风格的卷积网络视觉基线模型,高效涨点

💡该教程为改进进阶指南,包含大量的原创首发改进方式, 所有文章都是全网首发原创改进内容🚀💡本篇文章 基于 YOLOv5、YOLOv7芒果改进YOLO系列:芒果改进YOLOv7系列:超越ConvNeXt结构,原创结合Conv2Former改进结构,Transformer 风格的卷积网络视觉基线模型,高效涨点、…

idekctf2022部分web

前言 我爱idekctf&#xff01;&#xff01;&#xff01;&#xff01;有dockerfile真是太棒了 因为实在不会前端&#xff0c;所以暂时只复现非xss的题目 题目附件我都放在了这儿 Web/Readme 0x00 一个代码审计题&#xff0c;用的go语言&#xff0c;平常接触的不多&#xf…

机器学习知识总结 —— 19.朴素贝叶斯网络

文章目录贝叶斯概率简述朴素贝叶斯训练过程预测过程简单的说贝叶斯概率简述 在我写过的关于统计学相关文章 《概率论基础 —— 2. 条件概率、全概率、贝叶斯概率公式》 提到过一个很重要的概率公式—— 贝叶斯公式。其基本形式如下&#xff1a; P(xi∣Y)P(xi)P(Y∣xi)P(Y)P(x_…