我想知道当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
东西。
那么,什么是魔法呢?
1条答案
按热度按时间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。在这些行中:
字符串
尾随的“134”表示这是新文件的开始(1),它来自系统头,因此应抑制某些警告(3),并且它应被视为 Package 在
extern "C"
块中(4)。结尾的“2”表示在包含另一个文件后,它将返回到前一个文件。(显然,包含“/usr/include/stdc-predef.h”导致没有代码行,可能是因为该文件完全被未激活的#if
.#endif
对 Package 了。)...当其预处理器已删除注解时?
当GCC预处理删除注解时,它会保留新行字符,因此行距保持不变。例如,在处理输入时:
型
预处理器产生:
型
因此输出与输入具有相同的行数。因此,预处理后行号仍然正确。但是,列信息不是以这种方式传送的。请考虑以下代码:
型
当我用Clang 11.0.0编译它时,错误消息是:
型
正如我们所看到的,编译器知道错误从第20列开始。但是,当我使用
clang -E x.c >x.i
对其进行预处理,然后编译生成的x.i
文件时,错误消息是:型
这说明预处理器输出中不包含列信息。因此,我们可以得出结论,编译器在进行预处理和编译时,都会在内部维护这些信息。在现代的GCC和Clang中,预处理被集成到编译中;它实际上不是单独的处理步骤。
查看预处理集成到编译中的另一种方法是编译以下代码:
型
如果预处理是编译之前的一个单独步骤,则
#error
指令将导致打印一条消息,并导致进程退出。但是,当使用Clang编译时,编译器会先打印有关int foo(nuts)
行的消息,然后再打印有关#error
行的消息。这表明预处理与编译是交织在一起的;预处理是与编译一起逐行进行的,因此编译器在处理完前面的int foo(nuts);
行之前不会到达#error
指令。