当GCC的预处理器删除了注解时,GCC如何知道错误的位置(在源代码中)?

dsekswqp  于 2023-08-03  发布在  其他
关注(0)|答案(1)|浏览(120)

我想知道当GCC的预处理器已经删除注解时,GCC是如何知道错误在哪里(在源代码中)的?我谷歌了一下,但没找到。我会解释我的意思:
我有这样的C代码:

int main(void)
{
  return /* comment */ ) /* another comment */0;
}

字符串
')'字符(第24个字符)的位置有语法错误。然后我通过GCC预处理器(gcc -E main.c)过滤它,结果是:

# 0 "main.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "main.c"
int main(void)
{
  return ) 0;
}


好吧,我还是明白这些步骤的。预处理器已删除注解。但问题是。现在,由于删除了注解,语法错误位于第10个字符(而不是第24个字符)的位置。那么,它是如何知道语法错误的确切位置的呢?(如以下输出所示)

main.c: In function ‘main’:
main.c:3:24: error: expected expression before ‘)’ token
    3 |   return /* comment */ ) /* another comment */0;
      |                        ^
main.c:3:24: error: expected statement before ‘)’ token


我发现有一些带有#line标记的东西,但在预处理器输出中,没有这样的#line东西。
那么,什么是魔法呢?

qmelpv7a

qmelpv7a1#

the Stack Overflow question you link to中描述的#line预处理器指令是一个标准的C指令,用于设置编译器对当前源文件和行号的概念。它可以用于通过预处理来传递此信息,以便在预处理之后,编译器仍然具有关于代码行的起源的信息。它也可以被其他处理或生成源代码的工具(如YACC或Lex)使用,以提供有关在其输出中发现的代码在其输入文件中源自何处的信息。
但是,GCC使用其自身的非标准机制来传达此信息和其他信息。在您所显示的预处理器输出中,非标准指令# 1 "main.c"本质上等同于标准指令# line 1 "main.c";这两个文件都说下面的行来自文件“main. c”的第1行。
因此,原点线信息在显示的预处理器输出中完全可见。
但是,GCC形式允许additional information。在这些行中:

# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2

字符串
尾随的“134”表示这是新文件的开始(1),它来自系统头,因此应抑制某些警告(3),并且它应被视为 Package 在extern "C"块中(4)。结尾的“2”表示在包含另一个文件后,它将返回到前一个文件。(显然,包含“/usr/include/stdc-predef.h”导致没有代码行,可能是因为该文件完全被未激活的#if.#endif对 Package 了。)
...当其预处理器已删除注解时?
当GCC预处理删除注解时,它会保留新行字符,因此行距保持不变。例如,在处理输入时:

abc
/* Multiple-line comment
   consisting of
   three lines */
xyz


预处理器产生:

abc


xyz


因此输出与输入具有相同的行数。因此,预处理后行号仍然正确。但是,列信息不是以这种方式传送的。请考虑以下代码:

int foo/*comment*/(nuts);


当我用Clang 11.0.0编译它时,错误消息是:

x.c:1:20: error: a parameter list without types is only allowed in a function
      definition
int foo/*comment*/(nuts);
                   ^


正如我们所看到的,编译器知道错误从第20列开始。但是,当我使用clang -E x.c >x.i对其进行预处理,然后编译生成的x.i文件时,错误消息是:

x.c:1:10: error: a parameter list without types is only allowed in a function
      definition
int foo (nuts);
         ^


这说明预处理器输出中不包含列信息。因此,我们可以得出结论,编译器在进行预处理和编译时,都会在内部维护这些信息。在现代的GCC和Clang中,预处理被集成到编译中;它实际上不是单独的处理步骤。
查看预处理集成到编译中的另一种方法是编译以下代码:

int foo(nuts);
#error "Stop processing."


如果预处理是编译之前的一个单独步骤,则#error指令将导致打印一条消息,并导致进程退出。但是,当使用Clang编译时,编译器会先打印有关int foo(nuts)行的消息,然后再打印有关#error行的消息。这表明预处理与编译是交织在一起的;预处理是与编译一起逐行进行的,因此编译器在处理完前面的int foo(nuts);行之前不会到达#error指令。

相关问题