【问题标题】:What does it mean that the language of preprocessor directives is weakly related to the grammar of C?预处理器指令的语言与 C 的语法弱相关是什么意思?
【发布时间】:2017-07-27 07:49:09
【问题描述】:

Wikipedia article on the C Preprocessor 说:

预处理指令的语言与 C 的语法关系微弱,因此有时用于处理其他类型的文本文件。

预处理器的语言与 C 语法有何不同?有什么优势? C 预处理器是否已用于其他语言/用途?

它可以用来区分内联函数和宏吗,因为内联函数具有普通 C 函数的语法,而宏使用的语法略有不同?

【问题讨论】:

  • 它说它“弱相关”,这意味着它几乎没有相关性。
  • C 预处理器已用于其他目的。一个值得注意的是作为imake 系统的一部分,它与X-Windows 一起使用,但不包括X11R7.0。预处理器不能轻易地用于区分宏(它可以理解)和内联函数(它没有任何线索)。

标签: c c-preprocessor


【解决方案1】:

Wikipedia 文章并不是 C 编程语言的真正权威来源。 C preprocessor grammar C 语法的一部分。然而,它与phrase structure grammar 完全不同,即这两个根本不相关,除了它们都理解输入由 C 语言标记组成,(尽管 C 预处理器具有 preprocessing numbers 的概念,这意味着某些东西如123_abc 是合法的预处理令牌,但它不是有效的标识符)。

在完成预处理之后,开始使用短语结构语法进行翻译之前(预处理器指令已经被移除,宏被扩展等等),

每个预处理标记都转换为一个标记。 (C11 5.1.1.2p1 item 7)


对任何其他语言使用 C 预处理器确实是滥用。原因是预处理器要求文件包含适当的 C 预处理标记。它不适用于任何其他语言。即使是 C++ 及其最近的扩展,例如 raw string literals,也无法通过 C 预处理器进行预处理!

这是cpp (GNU C preprocessor) manuals的摘录:

C 预处理器仅适用于 C、C++ 和 Objective-C 源代码。 过去曾被滥用为将军 文本处理器。 它会阻塞不遵守 C 词法的输入 规则。例如,撇号将被解释为 字符常量,并导致错误。此外,你不能依赖它 保留对输入不重要的特征 C 族语言。 如果对 Makefile 进行了预处理,则所有硬标签 将被删除,Makefile 将不起作用。

【讨论】:

  • 我曾经尝试在 Delphi(即 Pascal)源代码上使用 C 预处理器。效果不好,因为 Pascal 有 'strings' 而不是 "strings"
【解决方案2】:

预处理器创建预处理标记,然后将其转换为 C 标记。

一般来说,转换是非常直接的,但并非总是如此。例如,如果您有一个条件预处理指令,其计算结果为 false,如

#if 0
   comments
#endif

然后在comments 中,您可以编写任何您想要的内容,它将在预处理令牌中进行转换,而这些令牌永远不会转换为 C 令牌,因此您可以像这样在 C 源文件中插入未注释的代码。

预处理器语言和 C 语言之间的唯一联系是许多标记的定义几乎相同,但并非总是如此。

例如,拥有像4MD 这样的预处理器编号(在ISO9899 标准中称为pp-numbers)是有效的,它们是有效的预处理器编号但不是有效的C 编号。使用 ## 运算符,您可以使用这些预处理数字获得有效的 C 标识符。例如

#define version 4A
#define name TEST_
#define VERSION(x, y) x##y
VERSION(name, version) <= this will be valid C identifier

预处理器的构想使其适用于任何语言以进行文本翻译,而不考虑 C。在 C 语言中,主要用于明确区分接口和实现。

【讨论】:

  • 谢谢,我想不出一个预处理令牌不是 C 令牌的例子。最初它是以这种方式构思的,但至少从 ANSI-C 开始,情况并非如此。一个更正不过,在#if 0 中,里面的东西仍然必须是预处理令牌,所以你不能在那里写you can't
  • @AnttiHaapala 你可以。来自非终结符preprocessing-token: 的 ISO9899(附件 A)的官方 Backus-Naur 表格的最后一个条目说:each non-white-space character that cannot be one of the above 换句话说,它存在一个预处理令牌pp-other,可以保留任何东西。我看到了非 C 代码插入到 C 文件中并且编译器不使用#if 0 处理的情况。
  • @alinsoar:但请参见第 6.4p3 节:“如果 ' 或 " 字符匹配最后一个类别,则行为未定义。”当然,gcc 抱怨(非致命的)@ 中的松散撇号987654330@ 部分。
【解决方案3】:

C 预处理器中的条件是有效的 C 表达式,因此预处理器和 C 语言本身之间的联系是密切的。

#define A (6)
#if A > 5
Here is a 6
#elif A < 0
# error
#endif

这扩展为无意义的 C,但可能是有意义的文本。

这是一个 6

虽然扩展的文本是无效的 C,但预处理器使用 C 的特性来扩展正确的条件行。 C 标准根据常量表达式定义了这一点:

来自C99 standard §6.6

6.10.1 条件包含

表单的预处理指令

# if constant-expression new-line group opt

# elif constant-expression new-line group opt

检查控制常量表达式的计算结果是否为非零。

这里是常量表达式的定义

6.6 常量表达式

语法:

constant-expression:
   conditional-expression

说明 常量表达式可以在翻译期间而不是运行时进行计算,因此可以在任何 可能是常数的地方。

约束 常量表达式不得包含赋值、递增、递减、函数调用或逗号运算符,除非 它们包含在未计算的子表达式中。

每个常量表达式都应计算为一个常量,该常量位于 其类型的可表示值范围。

鉴于上述情况,很明显预处理器需要有限形式的 C 语言表达式求值才能工作,因此需要了解 C 类型系统、语法和表达式语义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-30
    • 2014-11-07
    • 2022-01-12
    • 2020-08-07
    • 2016-12-03
    • 2011-09-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多