包含循环 - 链接器或编译器错误?

Kak*_*aka 0 c compiler-construction linker loops

我想知道包含文件的无限循环是否会导致编译器问题或链接器问题.我试过这个:

/* file : try.c */
#include "try1.c"
int main(void) {}

/* file : try1.c */
#include "try.c"
int a(void) { return 0; }
Run Code Online (Sandbox Code Playgroud)

编译命令是:

gcc -Wall try.c -o try
Run Code Online (Sandbox Code Playgroud)

这显然会导致很长的输出(像这样开始):

try.c:5:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try.c:1:0,
                 from try1.c:1,
                 from try.c:1:
try1.c:4:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try1.c:1:0,
                 from try.c:1:
try.c:5:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try.c:1:0:
try1.c:4:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
try.c:5:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try.c:2:0,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                   .
                   .
                   etc...
Run Code Online (Sandbox Code Playgroud)

嗯,显然这里有一个无限循环.但什么时候发生?在编译过程中还是链接器?我想你将在编译过程中告诉我,因为它将在这里定义多个具有相同名称的函数(因为循环),但不是unite文件在链接器进程中出现的部分(然后在那里只有一个文件没有编译问题)?

谢谢 !

Flo*_*ris 5

实际上,#include- 类型语句的扩展称为"预处理"步骤.我曾经认为这些步骤都是任何"编译"发生之前作为一个单独的步骤处理的,但@EricPostpischil在评论中指出(并举例说明它)这两件事 - 预处理和编译 - 似乎发生了并发(如源文件中的行顺序所指示的).换句话说,#命令的扩展("预处理器指令")是"在编译发生时"完成的.从这个意义上说,错误是一个"编译"错误; 但在我看来,说" #include预处理器处理的内容" 是有效的.当编译器处理"预处理器步骤"时,该行模糊.绝对不是引起问题的链接器 - 编译器在你进入该步骤之前就已经放弃了.

作为一般性评论,将#include一个.c文件放在另一个文件中并不是一个好习惯- 这就是链接器应该用于的内容.并且为了防止"递归包含",您经常.h会在编译器附带的文件中看到这样的结构:

#ifndef __MYINCLUDEFILE
#define __MYINCLUDEFILE
... put the body of the include file here
#endif
Run Code Online (Sandbox Code Playgroud)

这确保了包含文件只包含一次,即使它是从多个地方调用的(第一次包含它,__MYINCLUDEFILE变量将被定义;下次包含它时,将跳过整个函数体).一旦你对每个包含文件执行此操作,您所遇到的那种"递归陷阱"就不再发生了.

正如@wildplasser所指出的那样,语言和实现的使用_NAME__NAME保留 - 我使用它作为一个例子,因为你会在编译器附带的头文件中看到这样的结构.当您创建自己的.h文件时,您必须考虑另一个创建唯一标识符的约定.

  • @EricPostpischil - 我想这是一个语义问题."编译过程"(全部由gcc处理)包含许多步骤:预处理,编译,汇编,链接,加载.您通过单个步骤调用它的事实不会删除存在多个阶段的概念.我相信在第一行代码被"编译"之前,所有的`#`指令都被解释/扩展等.示例(非权威)链接:http://c-links.blogspot.com/2008/09/difference-between-preprocessor.html.你有一个反例吗? (2认同)