最近更新于 2024-08-07 07:57
测试环境
Windows 11 + VS 2022(MSVC)
Ubuntu 24.04 + GNU 13.2.0(g++)
研究
C 语言的 math.h 头文件中定义了一些数学常量,这是一个标准库。C 语言的标准库在 C++ 中通常提供了适配的头文件,命名为 C + C标准库名 + 去掉.h。math.h 在 C++ 中使用,可以引用 cmath 这个头文件。
使用 GNU 编译下面代码
#include <iostream>
#include <cmath>
int main()
{
std::cout << M_PI << std::endl;
}
编译成功,运行正常
然而同样的代码使用 VS 编译就报错了
于是对常量的定义进行追踪发现引用关系为:cmath->cstdlib->math.h->corecrt_math_defines.h
corecrt_math_defines.h 中定义了数学常量
但是 math.h 引用 corecrt_math_defines.h 是有条件的
需要定义 _USE_MATH_DEFINES 才会引用,于是代码添加了这个定义
#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
int main()
{
std::cout << M_PI << std::endl;
}
能成功运行了
但是如果把代码改成了
#include <iostream>
#define _USE_MATH_DEFINES
#include <cmath>
int main()
{
std::cout << M_PI << std::endl;
}
又说找不到 M_PI 了,那么就考虑是 iostream 中其实也直接或间接地引用了 cmath,等于第一次引用 cmath 发生在定义 _USE_MATH_DEFINES 之前,后面我再引用的已经无效了,于是测试
#define _USE_MATH_DEFINES
#include <iostream>
int main()
{
std::cout << M_PI << std::endl;
}
可以编译运行,证明 iostream 中已经直接或间接引用了 cmath。那么要保证数学常量能正常使用,就需要保证 _USE_MATH_DEFINES 定义在最前面,实际开发中引用的库非常多,不能保证别的头文件就没有引用 cmath。再一想,这还是单文件,只要放在最前面一定保证定义有效,如果是多源文件,引用的关系就非常复杂了,需要放置在所有源码的第一次引用 cmath 的前面的才有效。
最好的办法就是项目属性,放置到预处理器中,这样就是编译器一开始就给加上了
源文件中不需要添加定义了,也能保证正常引用
这个属于是编译器内部实现的差异,VS 需要用 _USE_MATH_DEFINES 来控制是否引用数学常量,而 GNU 是直接引用的。算是各有优劣,对于不清楚情况的,在 VS 里遇到就有一点懵,会想明明 GNU 里都能直接用的(我第一次遇到的时候就是这样),但是在一定程度上避免命名空间污染,有些项目里可能自己定义了同名的标识符会产生冲突,通过条件控制是否引用,默认不引用,这样就避免重名的问题。