【问题标题】:Preprocessor and compiler errors in CC 中的预处理器和编译器错误
【发布时间】:2020-10-12 10:16:18
【问题描述】:

当我在 C 语言中出现语法错误时,我如何知道它是预处理器错误还是编译器错误?

假设我输入这一行:"# include header.h"" 是该行的一部分,使其成为字符串文字)。

预处理器会出现问题,还是编译器会将其视为字符串而不将其分配给任何东西?

【问题讨论】:

  • 你只能用gcc -E运行预处理器(如果使用gcc),看看有没有问题
  • 通常从错误消息中可以很清楚地看出它是否是预处理问题,但如果你不确定你总是可以按照 Eugene 的建议在预处理后停止。那肯定会告诉你。
  • 编译器为什么要把它当作字符串处理?字符串文字由" 包围。在本例中," 是否计入该行或只是为了强调该行?
  • @P__J__ 通常是因为找不到头文件,或者宏的参数数量错误,或者没有#if#else。有很多例子,它们通常是非常明显的预处理问题。
  • “预处理器会出现问题,还是编译器会将其视为字符串而不将其分配给任何东西?” - 当您尝试时发生了什么?

标签: c string compiler-construction c-preprocessor preprocessor


【解决方案1】:

通常编译器输出不会区分“预处理器错误”和“编译器错误”,因为它们并不是真正的标准化术语。

所谓的“预处理”通常是形成预处理器标记,然后解析所有包含、编译指示和宏的过程。在 C 标准中,这种“预处理”大致对应于“翻译阶段”3 和 4:

  1. 源文件被分解为预处理标记和序列 空白字符(包括 cmets)。源文件不应以 部分预处理标记或部分注释。每条评论都替换为 一个空格字符。保留换行符。是否每个非空 除换行符以外的空白字符序列被保留或替换为 一个空格字符由实现定义。
  1. 预处理指令被执行,宏调用被扩展,并且 _Pragma 一元运算符表达式被执行。如果一个字符序列 匹配通用字符的语法名称由令牌产生 连接(6.10.3.3),行为未定义。 #include 预处理 指令使命名的头文件或源文件从阶段 1 开始处理 通过第 4 阶段,递归。然后删除所有预处理指令。

编译器显然不会抱怨在上述任一阶段中找到有效的字符串文字"# include header.h" - 字符串文字是有效的预处理器令牌。您所说的“预处理器错误”可能是在上述任何阶段发生的错误。

(这是一个简化的解释,还有很多其他稍微有趣的事情也在发生,比如三元组和换行符 \ 替换等)

但在这种情况下,我认为编译器会在第 7 阶段抱怨,强调我的:

  1. 分隔标记的空白字符不再重要。每个 预处理令牌转换为令牌。 生成的标记是 句法和语义分析并作为翻译单元进行翻译。

【讨论】:

    【解决方案2】:

    "预处理器会出现问题,还是编译器会将其视为字符串而不将其分配给任何东西?"

    我试过你的例子:

    "#include <stdio.h>"
    

    我收到以下错误:

    对于 GCC:

    "错误:字符串常量前的预期标识符或 '('"

    对于 Clang:

    "错误:预期的标识符或 '('"

    你可以看到here

    GCC 和 Clang 都将其视为字符串文字,这是合理的,因为 " 包围的字符序列被指定为字符串文字:

    “字符串字面量是用双引号括起来的零个或多个多字节字符的序列,如"xyz"。” 来源:ISO/IEC 9899:2018 (C18), §6.4.5/3。

    这个问题是编译器关心的,而不是预处理器。一般来说,由于宏是在编译之前扩展的,所以预处理器指令的不正确或失败通常也是编译器抱怨的问题。 C 预处理器通常没有明确的错误检测阶段。

    如果分配合适,f.e.:

    const char* p = "#include <stdio.h>";
    

    如果您使用在 header.h 中声明的变量、函数等,您可能*会收到有关这些变量/函数的未定义引用的错误,因为编译器/链接器无法查看/找到这些声明。

    *您是否收到错误还取决于该变量/函数的定义在源代码中使用之前是否可见,或者您如何链接多个源文件。


    "当我在 C 语言中出现语法错误时,我如何知道它是预处理器错误还是编译器错误?"

    如上所述,没有真正的预处理器错误,编译器涵盖了这些问题。预处理器并没有真正分析错误,它只是在扩展。通常,即使编译器评估语法问题,错误是否属于宏也很清楚。

    正如 Eugene 在 cmets 中所说,您可以在使用 GCC 的 -E 选项时查看代码的宏扩展版本,并测试扩展是否成功/按需要扩展。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多