在Visual Studio中,M_PI可以与math.h一起使用,但不能与cmath一起使用。

116

我正在使用Visual Studio 2010。我已经阅读过在C++中最好使用<cmath>而不是<math.h>

但是,在我正在尝试编写的程序中(Win32控制台应用程序,空项目),如果我写:

#define _USE_MATH_DEFINES
#include <math.h>

它可以编译通过,但如果我写

#define _USE_MATH_DEFINES
#include <cmath>

它失败并显示错误信息:

error C2065: 'M_PI' : undeclared identifier

这是正常的吗?如果我使用cmath或math.h会有影响吗?如果有,如何才能让它与cmath一起工作?

更新:如果在GUI中定义了_USE_MATH_DEFINES,它就可以工作。有任何线索为什么会发生这种情况吗?


你的源文件是 .c 还是 .cpp 后缀? - Swiss
1
瑞士:这里不应该有影响。 - rubenvb
非常奇怪...我可以确认我在VS2010中遇到了同样的问题...正在研究是什么阻止了定义的传递...它一定被某个地方取消定义了...但我无法弄清楚是在哪里。 - Goz
使用x86时,会报错C2065。 使用x64时,则不会出现错误。 - user2616989
7个回答

141

有趣的是,我在我的一个应用程序上检查了这个问题,结果得到了相同的错误。

我花了一些时间检查头文件,看是否有任何未定义_USE_MATH_DEFINES的内容,并没有发现。

因此,我移动了

#define _USE_MATH_DEFINES
#include <cmath>

要让它成为文件中的第一件事(如果您没有使用PCH,则必须在#include "stdafx.h"之后),然后它突然就编译成功了。

尝试将其移到页面更高的位置。虽然不确定为什么会导致问题。

编辑: 找到原因了。 #include <math.h>出现在cmath的头文件保护内部。这意味着在#includes列表中更靠上的某些内容正在包括cmath,但没有指定#definemath.h专门设计成可以用该定义再次包括它,现在已更改以添加M_PI等。而对于cmath则不是这种情况。因此,在包含任何其他内容之前,您需要确保您#define _USE_MATH_DEFINES。希望这能为您澄清疑惑 :)

如果失败了,请只包括math.h,正如先前指出的,您正在使用非标准C / C ++ :)

编辑2: 或者如David在评论中指出的那样,只需创建一个定义该值的常量,您将获得更多可移植性:)


@Als:不是那样的...我已经破解了它并在上面的编辑中解释了 :) - Goz
那是要做的第一件事,将其放在所有其他头文件之上,我要求OP也这样做...无论如何,由于你的答案说明了为什么它应该在标准头文件之前,所以我会删除我的答案。 - Alok Save
3
例如,如果在你使用 #include <math.h> 之前发生了其他事情,你就会得到这种行为。话虽如此,标准中并没有规定 <cmath> 必须包含 <math.h>,也没有规定它必须启用 <math.h> 中的非标准定义。不幸的是,M_PI 是非标准的。为了可移植性,最好的做法是自己定义它。更好的方法是将其定义为 const static double 而不是 #defined 值。 - David Hammen
@Als:但如果从预编译头文件中调用了其中一个 #include,而 cmath 被包含在内,他就会遇到麻烦... - Goz
1
@David Hammen:同意。自己定义肯定是最具可移植性的选项 :) - Goz
显示剩余4条评论

19

考虑将 /D_USE_MATH_DEFINES 开关添加到编译命令行中,或在项目设置中定义宏。这将把该符号拖到所有可达到的包含文件和源文件的黑暗角落,使您的源代码适用于多个平台,并保持源代码干净。如果您在整个项目中全局设置它,以后在新文件中也不会忘记它。


这可能是在使用VisualStudio时的一个好答案,但请注意,当通过Matlab mex命令行编译时,它并没有解决我的问题(我使用了mex -D_USE_MATH_DEFINES)。只有在某个Matlab mexoptions文件中添加/Y-才有帮助... - aka.nice

12

这对我有效:

#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>

using namespace std;

int main()
{
    cout << M_PI << endl;

    return 0;
}

编译并打印pi,如下命令所示:cl /O2 main.cpp /link /out:test.exe

你发布的代码和你尝试编译的代码之间必须存在不匹配。

确保在#define之前没有引入任何预编译头文件。


你正在使用哪个版本的VisualStudio? - Goz
我使用Visual C++ 2010 Express Edition的命令行编译器运行了相同的程序,一切都正常。唯一的区别是,我使用<cstdio>中的std::printf()而不是<iostream>中的std::cout。 - user539810
5
我已经明白了...这是因为在cmath的头文件保护中调用了maths.h...所以maths.h已经在早期的头文件中被包含,没有设置#define :) - Goz

7
使用CMake,只需要这样:
add_compile_definitions(_USE_MATH_DEFINES)

CMakeLists.txt 文件中。

6
这在使用预编译头文件创建控制台或窗口应用程序时,仍然是VS Community 2015和2017中的问题。预编译头文件似乎会在任何#include之前加载,因此即使#define _USE_MATH_DEFINES是第一行,也无法编译。将cmath替换为math.h并没有区别。
我能找到的唯一解决方案是从空项目开始(对于简单的控制台或嵌入式系统应用程序),或者将/Y-添加到命令行参数中,以关闭预编译头文件的加载。
有关禁用预编译头的信息,请参见例如https://msdn.microsoft.com/en-us/library/1hy7a92h.aspx 如果微软可以改变/修复这个问题就太好了。我在一所大学教授入门编程课程,向新手解释这个问题直到他们犯了错误并且花费了一个下午才能理解。

我确认对于C语言代码#include <math.h>,黑客术/Y-对我有效。 - aka.nice
1
这在VS中根本不是问题。 _USE_MATH_DEFINES 应该在包含任何头文件之前定义。通常通过项目设置或配置头文件来实现。错误的做法是仅将其放在第一行,以使其在所有头文件之前被定义。 - user7860670

2
根据user7860670的建议,右键单击项目,选择属性,导航到C/C++ -> 预处理器并将_USE_MATH_DEFINES添加到预处理器定义中。

这对我有用。


1
根据 Microsoft 关于 数学常量 的文档:

当您的项目以 Release 模式构建时,文件 ATLComTime.h 会在包含 math.h。如果您在一个也包含 ATLComTime.h 的项目中使用一个或多个数学常量,则必须在包含 ATLComTime.h 之前定义 _USE_MATH_DEFINES

文件 ATLComTime.h 可能会间接地包含在您的项目中。在我的情况下,包含的可能顺序如下:

项目的 "stdafx.h"<afxdtctl.h><afxdisp.h><ATLComTime.h><math.h>


这可能解释了为什么/Y-(禁用stdafx.h)可以解决问题,但仍需解释为什么在默认编译器设置中提供“-D_USE_MATH_DEFINES”不能足以解决问题...由于编译是通过Matlab mex命令进行的,对于我的问题来说,跟踪并不那么明显... - aka.nice

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接