为什么#pragma 被认为是预处理器指令?

8

我知道它以#开头,遵循预处理指令格式。但是预处理器真的关心它吗? 我所知道的#pragma pack#pragma once和所有其他指令都是由编译器处理的。在这个问题的评论中甚至有一个说明#pragma指令在预处理阶段之后也会存在。

  1. 既然#pragma是为编译器而设计的,为什么它被认为是预处理指令? 它只是因为以#开头吗?
  2. 预处理器是否真正对#pragma做了些什么?

1
这个答案解释了一切。 - kesarling He-Him
4
预处理器需要使用#pragma once指令。 - interjay
3个回答

7
为什么#pragma被认为是预处理指令?
因为C标准规定了这一点。它在预处理指令章节中进行了说明,C17 6.10.6。除此之外,标准故意模糊了#pragma应该做什么,因为整个目的是做一些特定于编译器的事情。或者在pragma未被识别的情况下忽略它。
某个编译器如何处理pragma内部的内容并没有规定。
显然,有些pragma需要进行预处理,特别是启用/禁用某些编译器行为的种类,如#pragma warning ...等。许多pragma必须在预处理期间进行评估,否则编译器将不知道如何编译代码。
预处理器是否真的会对#pragma进行处理?
是的,在第四个翻译阶段中对其进行评估:“预处理指令被执行,宏调用被展开,并且执行_Pragma一元运算符表达式。”
请注意,将预处理器与编译器分开是一个理论上的模型。实际上,预处理器和编译器通常相互紧密集成。

预处理是在任何编译开始之前进行的源到源的翻译。因此,#pragma warning不能被预处理,否则编译器将永远看不到它。 - OrangeDog
所以 pragma 似乎是一种影响编译器行为的方式,但与编译器标志不同的是,该行为可以局部于代码中。例如,对于一个结构类型进行打包,而对另一个结构类型不进行打包。 - Paul Ogilvie
@OrangeDog 这个回答的最后一句话。预处理器不是一个神奇的外部实体,与编译器完全分离。 - Lundin
@Lundin 这并不是理论上的,你可以单独运行预处理器,然后将其输出传递给编译器,它应该具有相同的行为。在正常构建中,它们一起运行以优化速度,例如通过进行单个解析,然后修改AST而不是直接修改源代码。 - OrangeDog
@OrangeDog:如果支持#pragma warning#pragma error,则在预处理阶段进行处理,该消息将以编译器诊断的相同格式输出给用户,但无需通知编译器。需要注意的是,#pragma error应该使用非零状态码停止编译过程。 - chqrlie
根据C 2018 6.10.6,没有STDCpragma指令会执行“特定于编译器”的操作(实现定义)。带有STDCFP_CONTRACTFENV_ACCESSCX_LIMITED_RANGEpragma指令会执行标准定义的操作。 - Eric Postpischil

2

#pragma once需要由预处理器处理,因为它的工作是使用预处理指令#include确保一个文件在给定位置只被包含一次,从而替代include guards。另一方面,#pragma pack需要无损地通过预处理器,因为它是一个关于如何在内存中布局数据的编译器指令。


2
直接回答你的问题:
1. 大多数pragma(除了STDC FENV_ACCESS、STDC FP_CONTRACT和STDC CX_LIMITED_RANGE)根本不是C标准的一部分,因此它们是否是“预处理指令”并不重要,编译器可以以任何方式处理它们。对于某些pragma,在预处理阶段处理它们是有意义的,而对于其他一些则没有。 pragma的主要思想是它们可能会从预处理阶段开始影响编译过程,但与宏不同,它们不会扩展为任何内容。
2. 是的,例如在其他答案中解释的#pragma once的情况。但是,这再次是实现特定的,而非标准规定的。

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