在C89标准中使用M_PI

30
我正在使用C语言,试图访问常量M_PI(3.14159...)。我已经导入了math.h头文件,但是M_PI常量仍然未定义。通过在StackOverflow上搜索,我发现需要在代码中添加#define _USE_MATH_DEFINES(请参见下面的示例代码)。这在正常编译时可以正常工作,但是我需要能够使用std=c89标志进行编译以完成我的工作。
如何从一些C89代码中访问M_PI?
4个回答

41
一个符合标准的库文件math.h不仅不是必须的,而且实际上不能默认定义M_PI。在这个上下文中,“默认”意味着M_PI必须只通过编译器特定的技巧定义,最常见的是通过使用保留标识符的未定义行为。
只需自己定义常量(您可以自由使用名称M_PI,但如果您想能够使用非符合性编译器编译代码,则必须首先检查是否已经定义了M_PI)。基于约定,请勿将M_PI定义为除(近似)π之外的任何内容。

6
这句话的意思是,这只是因为标准库实现不允许“污染”命名空间(也就是必须按照标准仅使用保留标识符)的结果。请注意,我已经尽力使翻译更易懂,但没有改变原意。 - eq-
是这样吗?这意味着 C 语言的实现永远无法同时符合 C89 和 C99 的要求,因为 C99 向库中添加了标识符。 - Fred Foo
2
@larsmans:C99小心地添加了在C89中保留给实现的标识符(特别是请参阅C89中的4.13未来库方向部分),或者在未在C89中定义的新头文件中。但是,在C89和C99之间存在一些不同的库行为的边角情况,例如strtod(),它必须在C99中处理十六进制浮点数,而在C89中则不能。 - caf
1
@FredFoo 没错,无法同时符合C89和C99标准。这就是为什么我们有编译器开关来选择其中之一的原因。C89实现可以提供一些C99功能作为符合扩展;显而易见的情况是允许程序调用C99函数或使用long long(只要发出诊断)。 - Kaz

31

我会选择

#ifndef M_PI
#    define M_PI 3.14159265358979323846
#endif

我必须做这样的事情,而不是保证π已定义,这让我感到愤怒。 - user16217248
1
@user16217248,正如第一个答案中所述,符合规范的实现实际上 不得 定义 M_PI,因此可以保证它未定义。我仍然会选择这个答案中的代码,因为首先检查它是否已经定义并没有任何伤害。 - Sven Marnach
@sven,实际上是第三个答案指出默认情况下不应定义M_PI。也许这个答案对你来说是首先显示的,但请记住,对其他读者来说可能并非如此(并且根据用户设置可能会发生变化)。 - Toby Speight

9
我看不出这里有什么问题;-std=c89和_USE_MATH_DEFINES之间没有不兼容性,前者定义编译器将编译哪种语言,后者定义激活math.h的哪些部分。
被激活的那些部分并不是ISO C标准库的一部分,但这与不是标准C语言并不相同。在C中,语言和库是不同的实体。如果您在自己的头文件中定义了自己的宏,它就不会比定义_USE_MATH_DEFINES更不符合C89。
但我建议您在命令行中定义宏,而不是在代码中定义。
-std=c89 -D_USE_MATH_DEFINES

如果你遇到一个没有定义M_PI的math.h实现,那么可以通过类似使用命令行定义的宏来轻松解决,无需修改代码:
-std=c89 -DM_PI=3.14159265358979323846

1
_USE_* 是 glibc 内部的一部分,只应由 features.h 定义,作为某些公共特性测试宏被定义的结果。使用 -D_GNU_SOURCE 或更具可移植性的 -D_XOPEN_SOURCE=700 或类似选项。 - R.. GitHub STOP HELPING ICE
@R..:在StackOverflow上找到宏后,正是OP决定使用它的-不幸的是他没有指明具体位置。GNU文档建议定义"_BSD_SOURCE_XOPEN_SOURCE=500,或更一般的功能选择宏",而Microsoft的C运行时文档则建议定义_USE_MATH_DEFINES,因此为了兼容性,我会坚持后者;问题没有提到任何特定的编译器。 - Clifford

9

M_PI 不是 C 标准所必须的,它只是一个常见的扩展。因此,如果你想符合标准,就不应该依赖它。然而,你可以很容易地为它定义自己的 #define,据我上次检查时它是一个普适常数,所以不会引起太多混淆。:)


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