初始化

记录技术与生活

C++ 使用 M_PI 宏 undefined 问题分析

我们开学到现在一直在学C++, 用的编译器是VS2017, 昨天作业里面有一道题让使用 cmath 中定义的宏 M_PI 去计算圆的周长

任务很简单, 于是写下代码

可是还没见运行, VS 的红色下划线就大大的画在了 M_PI 这个宏底下, 鼠标移上去一看是个undefined, 当时我知道肯定是还要做点别的工作, 上手去Google一搜, 原来是使用 cmath 中定义的非标准常量需要定义 _USE_MATH_DEFINES 宏, OK, 代码变成了如下模样:

以为 VS 日常卡顿得我慢慢的失去了信心 – 很奇怪, 还是有undefined错误
于是上StackOverflow查了查, 查到了这样的一个帖子:
M_PI works with math.h but not with cmath in Visual Studio

最后题主自己解决了问题, 说是把 #define _USE_MATH_DEFINES 移到首行就好了
当时我就觉得怎么有这么玄学的事情, 但是, 世间玄学的事情还真的多, 这次 VS 没有日常卡顿, 红标直接消失

虽说问题解决了, 但是我很好奇是什么原因导致的如此玄学的问题, 找了一遍Google, 也没找到有详细说这个问题的

我当时想, 既然要移动到 #include iostream 之前, 那么肯定是 iostream 的引用导致的这个问题, 可是标准输入输出库会有什么问题呢?

我打开了 iostream 文件, 并没有发现关于数学常量的宏定义, 于是改变思路, 先去寻找定义这个 M_PI 宏的文件, 从 cmath 出发, 经过一番寻找终于找到了 M_PI 宏定义和条件编译 #define _USE_MATH_DEFINES 的位置,分别在:

以及包含的 correcrt_math_defines.h:

找到定义和条件编译选项的宏之后, 我开始分析预编译器的引用过程: 我首先打开了 iostream, 在里面只有一个引用 istream, 于是打开它接着逐级寻找所引用包含的文件, 终于在这样的包含之中发现了线索:
iostream -> istream -> ostream -> ios -> xlocnum

在最后一级的 xlocnum 中包含了 cmath, 这一刻我终于明白了为什么要把条件编译的开关定义在引用 iostream 之前:
如果将宏定义放在引用输入输出库之后, 预编译器大概是这样工作的:

  1. 首先, 预编译器按照首行的指令引用 iostream 头文件
  2. 然后预编译器根据引用逐级的扩展文件, 最终到了 xlocnum 头文件
  3. 预编译器根据其中的指令去拓展 cmath 头文件
  4. 又是一番寻找加载符号, 从 cmath 找到了 math.h
  5. 注意, 这个时候我们还没有定义 _USE_MATH_DEFINES 编译器便没有加载 corecrt_math_defines.h 中的符号
  6. 等到以上的 iostream 加载完成之后, 预编译器定义了宏 _USE_MATH_DEFINES
  7. 根据第三行的指令, 预编译器试图加载 cmath 但是发现其实自己已经加载过了, 于是跳过了

于是最终的结果是条件编译选项并没有被执行, 即使我们定义了宏 _USE_MATH_DEFINES

为了验证我的想法, 我使用编译 /P 选项将预编译结果写到一个 .i 文件里:

查看预编译的结果, 在其中找到了这样的信息:

可以看到, 预编译器在一路加载的过程中遇到了 stdlib.h 并从这里跳转到了 math.h 由于 math.h 的首行定义是引用 correc_math.h 预编译器开始加载其中的符号, 但是根据对整份文件的搜索(结果文件太大了, 有60000行之多), 从 这里跳转出去之后并没有再回到 math.h 而是开始加载其余的内容和展开函数之类的工作, 因为整个 math.h 的内容只有一个引用和条件编译对于 corecrt_math_defines.h 的引用, 所以可以知道这个 _USE_MATH_DEFINES 的常量定义并没有生效.

而当我搜索所有关于源文件代码 source.cpp 的预编译结果时只有两个:

这里验证了, 第二行的 cmath 加载由于在加载 iostream 的过程中已经完成, 预编译器直接对这一行进行了忽略(甚至在源文件中定义的 _USE_MATH_DEFINES 也一并被忽略了…)

不得不感叹一句, 现代的编译器是真的太智能了

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.