为什么 __USE_MISC在包含stdio.h时会被取消定义,M_PI在包含math.h时会被取消定义?

前端开发 2026-07-12

我有如下代码:

#define __USE_MISC

#ifdef __USE_MISC
#pragma message("C Preprocessor got here!")
#endif

#include <stdio.h>

#ifdef __USE_MISC
#pragma message("C Preprocessor made it here too!")
#endif

#include <math.h>

int main(void) {
  double x = M_PI;
  printf("x = %lf \n", x);
  return 0;
}

因此,当我按如下方式编译:

$ gcc test.c -std=c23 -Wall -pedantic
test.c:4:9: note: ‘#pragma message: C Preprocessor got here!’
    4 | #pragma message("C Preprocessor got here!")
      |         ^~~~~~~
test.c: In function ‘main’:
test.c:16:14: error: ‘M_PI’ undeclared (first use in this function)
   16 |   double x = M_PI;
      |              ^~~~
test.c:16:14: note: each undeclared identifier is reported only once for each function it appears in

为什么使用 stdio.h 会使 __USE_MISC 的宏定义变成未定义?如果改为使用如下方式,为什么就消失了:

$ gcc test.c  -Wall -pedantic
test.c:4:9: note: ‘#pragma message: C Preprocessor got here!’
    4 | #pragma message("C Preprocessor got here!")
      |         ^~~~~~~
test.c:10:9: note: ‘#pragma message: C Preprocessor made it here too!’
   10 | #pragma message("C Preprocessor made it here too!")
      |         ^~~~~~~

来自 What is __USE_MISC macro used for? 我理解到,某个位置的 __USE_MISC 可能会被取消定义,但这个文件至少并非直接被 stdio.h 调用,尽管有不少头文件可能会被调用。

更新:我理解所有以USE开头的宏在 #include<stdio.h> 时都会被取消定义,但我也尝试了以下方法:

#define __USE_MISC

#include <math.h>

#ifndef M_PI
#pragma message("C Preprocessor can not find M_PI!")
#endif

#ifndef __USE_MISC
#pragma message("C Preprocessor finds __USE_MISC to be undefined")
#endif

int main(void) {
  double x = M_PI;
  x += 1;
}

因此,在这里,似乎仅通过对 #include<math.h> 的调用,__USE_MISCM_PI 也会被取消定义

test.c:6:9: note: ‘#pragma message: C Preprocessor can not find M_PI!’
    6 | #pragma message("C Preprocessor can not find M_PI!")
      |         ^~~~~~~
test.c:10:9: note: ‘#pragma message: C Preprocessor finds __USE_MISC to be undefined’
   10 | #pragma message("C Preprocessor finds __USE_MISC to be undefined")
      |         ^~~~~~~
test.c: In function ‘main’:
test.c:14:14: error: ‘M_PI’ undeclared (first use in this function)
   14 |   double x = M_PI;
      |              ^~~~
test.c:14:14: note: each undeclared identifier is reported only once for each function it appears in

为什么在这里会发生这种情况,而如果在对 __USE_MISC 进行规定之前先包含 #include<stdio.h>,就不会发生呢?

另外,如果我去掉 std(顺便说一句,即使我指定任何特定标准,从 c89c90c99c17c23 这些都是我尝试过的选项),问题仍然存在?即使指定 -ansi 也有同样的问题。

如果用户不应该指定 __USE_MISC,那么我该如何让 M_PI 被定义呢?

先对您的帮助表示感谢!

解决方案

如何让 M_PI 被定义,若 __USE_MISC 不应该由用户来指定?

这里需要理解的一个关键点是 M_PI 并不是由C 语言的任何版本规定的,它的形式也不是保留的。这意味着,在没有某些扩展或在C 语言本身之上增加的额外规定的情况下,C程序可以将该标识符用于自己的目的——变量名、函数名、结构成员名等。例如,它可能会声明 const unsigned int M_PI = 0x03A0;,如果 M_PI 被定义为扩展为浮点常量的宏,就会出问题。GCC的严格一致性模式,比如 -std=c23-ansi,的目标是让GCC接受严格符合所指定语言版本的每个程序,这与把 M_PI 作为预定义宏提供的做法不一致。

另一方面,M_PI 传统上在UNIX类系统的C 实现中由 math.h 定义,并且是POSIX规定的一部分。GCC与 Glibc的设计首要目标是面向GNU的 UNIX类环境,因此默认情况下,它们确实在 math.h 中提供了一个 M_PI 的定义。-std=c23 把这点关掉了,因此一个可选的做法就是简单地省略它。

另一种选择是使用GCC的扩展模式中的一种,而不是严格的合规模式。例如,-std=gnu23。GCC的默认模式是这些模式之一(具体是哪一种随GCC版本而异)。

再一个选择是在需要的源文件中定义一个合适的特征测试宏。__USE_MISC 不适合用于此,因为它是为实现者保留的,GCC和 Glibc也没有在源文件中记录它的用途。不过,Glibc确实 文档 了若干特征测试宏,这些宏是合适的,其中一些也会与其他C 实现相关。将这些特征测试宏中的任意一个或多个与 gcc -std=c23 一起使用,在GCC 15.2.1与 Glibc 2.42的组合下对我有效(你可以选择一个或多个):

#define _XOPEN_SOURCE
#define _GNU_SOURCE
#define _DEFAULT_SOURCE

这些特征测试宏应放在源文件的最顶部,位于任何 #include 指令之前。每一个都会通过 math.h 暴露对 M_PI 的定义,但它们启用的其他库扩展不同。

或者你也可以自己定义一个,这是最具可移植性的选项。例如,

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章