Wol*_*olf 10 c gcc c-preprocessor
我想知道当 GCC 的预处理器删除了注释时,它如何知道错误在哪里(在源代码中)?我用谷歌搜索了它,但找不到它。我将解释我的意思:
\n我有这样的C代码:
\nint main(void)\n{\n return /* comment */ ) /* another comment */0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\')\' 字符(第 24 个字符)位置存在语法错误。然后我通过 GCC 预处理器 ( gcc -E main.c
) 对其进行过滤,结果是:
# 0 "main.c"\n# 0 "<built-in>"\n# 0 "<command-line>"\n# 1 "/usr/include/stdc-predef.h" 1 3 4\n# 0 "<command-line>" 2\n# 1 "main.c"\nint main(void)\n{\n return ) 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n好吧,我还是明白这些步骤。预处理器已删除注释。但事情是这样的。现在语法错误出现在第 10 个字符的位置(不是第 24 个字符),因为注释被删除了。那么它如何知道语法错误到底在哪里呢?(正如我们在下面的输出中看到的)
\nmain.c: In function \xe2\x80\x98main\xe2\x80\x99:\nmain.c:3:24: error: expected expression before \xe2\x80\x98)\xe2\x80\x99 token\n 3 | return /* comment */ ) /* another comment */0;\n | ^\nmain.c:3:24: error: expected statement before \xe2\x80\x98)\xe2\x80\x99 token\n
Run Code Online (Sandbox Code Playgroud)\n我发现有一些带有#line
标记的东西,但在预处理器输出中,没有这样的#line
东西。
那么,到底有什么魔力呢?
\nEri*_*hil 10
您链接到的堆栈溢出问题#
line
中描述的预处理器指令是一个标准 C 指令,用于设置当前源文件和行号的编译器\xe2\x80\x99s 概念。它可用于通过预处理传递此信息,以便在预处理后,编译器仍然拥有有关代码行来源的信息。它还可以被处理或生成源代码的其他工具(例如 YACC 或 Lex)使用,以提供有关在其输出中找到的代码源自其输入文件的位置的信息。
然而,GCC 使用自己的非标准机制来传达此信息和附加信息。在您显示的预处理器输出中,非标准指令# 1 "main.c"
本质上等同于标准指令# line 1 "main.c"
;两者都说以下行来自文件 \xe2\x80\x9cmain.c\xe2\x80\x9d 的第 1 行。
因此,原始行信息在您显示的预处理器输出中完全可见。
\n然而,GCC 表格允许提供附加信息。在这些行中:
\n# 1 "/usr/include/stdc-predef.h" 1 3 4\n# 0 "<command-line>" 2\n
Run Code Online (Sandbox Code Playgroud)\n结尾的 \xe2\x80\x9c1 3 4\xe2\x80\x9d 意味着这是一个新文件的开始 (1),它来自系统标头,因此应抑制某些警告 (3),并且应对其进行处理包裹在extern "C"
块 (4) 中。尾随 \xe2\x80\x9c2\xe2\x80\x9d 表示它在包含另一个文件后返回到先前的文件。(显然,包含 \xe2\x80\x9c/usr/include/stdc-predef.h\xe2\x80\x9d 导致没有代码行,可能是因为该文件完全被 \ #if
xe2\x80\xa6对#endif
包裹未激活。)
\n\n\xe2\x80\xa6 当其预处理器删除注释时?
\n
当 GCC 预处理删除注释时,它会保留换行符,因此行距保持不变。例如,在处理输入时:
\nabc\n/* Multiple-line comment\n consisting of\n three lines */\nxyz\n
Run Code Online (Sandbox Code Playgroud)\n预处理器产生:
\nabc\n\n\n\nxyz\n
Run Code Online (Sandbox Code Playgroud)\n因此输出的行数与输入的行数相同。因此预处理后行号保持正确。然而,列信息不是以这种方式传达的。考虑这段代码:
\nint foo/*comment*/(nuts);\n
Run Code Online (Sandbox Code Playgroud)\n当我用 Clang 11.0.0 编译它时,错误消息是:
\nx.c:1:20: error: a parameter list without types is only allowed in a function\n definition\nint foo/*comment*/(nuts);\n ^\n
Run Code Online (Sandbox Code Playgroud)\n正如我们所看到的,编译器知道错误从第 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\n definition\nint foo (nuts);\n ^\n
Run Code Online (Sandbox Code Playgroud)\n这表明预处理器输出中不包含列信息。因此,我们可以得出结论,编译器在进行预处理和编译时会在内部维护这些信息。在现代GCC和Clang中,预处理被集成到编译中;它实际上不是一个单独的处理步骤。
\n查看预处理集成到编译中的另一种方法是编译此代码:
\nint foo(nuts);\n#error "Stop processing."\n
Run Code Online (Sandbox Code Playgroud)\n如果预处理是编译之前的单独步骤,则该#error
指令将导致打印一条消息并导致进程退出。但是,当使用 Clang 对其进行编译时,编译器首先打印有关该行的消息int foo(nuts)
,然后打印该#error
行的消息。这表明预处理与编译是交织在一起的;预处理是与编译一起逐行完成的,因此编译器在#error
处理完前一行之前不会到达该指令int foo(nuts);
。