【问题标题】:Is it safe to run the C preprocessor several times on the same source?在同一个源上多次运行 C 预处理器是否安全?
【发布时间】:2020-11-28 01:50:25
【问题描述】:

根据我的经验,C 预处理器在以前预处理的源上运行时只是表现为无操作。但是这种行为是由标准保证的吗?或者一个实现可能有一个预处理器来修改以前预处理的代码,例如删除/修改行指令,或者执行其他可能混淆编译器的修改?

【问题讨论】:

  • 你是如何调用预处理器的,准确地说?它通常是编译器的一部分,您不能手动运行它。此外,通过 C 预处理器的文件不会覆盖自身。
  • 我的意思是多次调用预处理器。例如cpp src.c > prepro.c 然后cpp prepro.c > prepro2.c
  • 预处理器仅修改其输出(相对于其输入)以响应预处理器指令;编译器(接收预处理器的输出)将不知道如何处理任何预处理器指令,因此推测预处理器的输出中永远不会有任何预处理器指令(如果我们期望编译器能够使用代码)。因此,我认为可以有效地保证通过预处理器运行已经预处理的代码将是无操作的,即使 C 标准中没有这样说明。
  • 您在上面评论中的示例只会生成相同文件的两个副本。它相当于copy test.txt test1.txt copy test.txt test2.txt 的命令行。两个文件内容的二进制比较将与您的示例中的两个文件完全相同。
  • 一些 clang 开发者似乎认为应该允许这样做来消除一些警告或生成新警告:bugs.llvm.org/show_bug.cgi?id=39367#c1

标签: c c-preprocessor


【解决方案1】:

一般来说,通过cpp 进行的预处理不能保证是幂等的(第一次运行后的noop)。一个简单的反例:

#define X #define Y z
X
Y

第一次调用将产生:

 #define Y z
Y

第二个:

z

话虽如此,有效的 C 代码不应该做这样的事情(因为输出不会是编译器下一阶段的有效输入)。

此外,根据您要执行的操作,cpp 有类似-fpreprocessed 的选项可能会有所帮助。

【讨论】:

  • Cop 不是 c 编译器。
  • @P__JsupportswomeninPoland OP 评论说他/她正在使用cpp
  • 我认为你的意思是不能保证它是幂等的。当然,也不能保证它是空操作,因为预计预处理器实际上会做某事(除非源没有预处理器指令或强制宏)。但这并不奇怪
  • @rici 这是正确的术语,是的。我试图用OP自己的话。谢谢!
  • 我认为有更多“实用”的答案是行不通的。一个明显的问题是预定义的宏。您需要能够完全关闭它们才能将预处理的源重新输入,否则预处理器输出中它们的任何实例都会被双倍扩展。我记得看到/制作了更好的例子,但现在想不起来。
【解决方案2】:

该标准没有将“预处理器”定义为单独的组件。最接近的是第 5.1.1.2 节中对翻译过程第 4 阶段的描述:

  1. 执行预处理指令,扩展宏调用,并 _Pragma 一元运算符表达式被执行。如果一个字符序列 匹配通用字符的语法名称由令牌产生 连接(6.10.3.3),行为未定义。 #include 预处理 指令使命名的头文件或源文件从阶段 1 开始处理 通过第 4 阶段,递归。然后删除所有预处理指令。

但是,该部分中定义的翻译阶段是不可分离的,也不保证它们相互独立:

实现应该表现得好像这些单独的阶段发生了一样,即使在实践中许多通常是折叠在一起的。源文件、翻译单元和翻译的翻译单元不一定要存储为文件,这些实体和任何外部表示之间也不需要任何一一对应的关系。描述只是概念性的,并没有具体说明 具体实施。 (同一部分的脚注 6。)

因此,没有预期的机制可以以任何形式提取翻译阶段 1-4 的结果,更不用说作为文本文件了——事实上,如果翻译阶段按照描述的方式实现,那么阶段 4 的输出将是一个标记序列——也没有一种机制可以将该输出反馈给翻译器。

换句话说,您可能有一些自称为预处理器的工具,它甚至可能是编译器套件的一部分。但是该工具的行为超出了 C 标准的范围。因此,标准根本没有任何保证。

顺便说一句,如果从第 4 阶段出来的令牌流被天真地转换为文本,它可能无法正确保留令牌边界。大多数预处理器工具会在可能发生这种情况的地方注入额外的空白。至少在大多数情况下,这允许将工具的输出输入编译器。 (请参阅 @acorn's answer 以了解此方法无法正常工作的示例。)但此行为既不是标准要求的,也不是标准规定的。

【讨论】:

    猜你喜欢
    • 2011-07-02
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 2011-08-08
    • 1970-01-01
    相关资源
    最近更新 更多