Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 包含循环链接器或编译器错误?_C_Loops_Compiler Construction_Linker - Fatal编程技术网

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

C 包含循环链接器或编译器错误?,c,loops,compiler-construction,linker,C,Loops,Compiler Construction,Linker,我想知道包含文件的无限循环是否会导致编译器问题或链接器问题。 我试过这个: /* file : try.c */ #include "try1.c" int main(void) {} /* file : try1.c */ #include "try.c" int a(void) { return 0; } 要编译的命令是: gcc -Wall try.c -o try 这显然会导致很长的输出(开始时如下所示): 很明显,这里有一个无限循环。但是什么时候会发生呢?在编译过程中还是在链接器

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

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

/* file : try1.c */
#include "try.c"
int a(void) { return 0; }
要编译的命令是:

gcc -Wall try.c -o try
这显然会导致很长的输出(开始时如下所示):

很明显,这里有一个无限循环。但是什么时候会发生呢?在编译过程中还是在链接器过程中?我想你会在编译过程中告诉我,因为它会在这里定义多个同名函数(因为循环),但是联合文件的部分不是在链接器过程中发生的吗(那么,只有一个文件没有编译问题)


谢谢

实际上,
#include
类型语句的扩展称为“预处理”步骤。我曾经认为,在任何“编译”发生之前,这些步骤都是作为一个单独的步骤来处理的,但是@EricPostpischil在评论中指出(并给出了一个例子来证明),预处理和编译这两件事似乎是同时发生的(正如源文件中的行顺序所指示的)。换句话说,
#
命令(“预处理器指令”)的扩展是“在编译时”完成的。从这个意义上说,这个错误是一个“编译”错误;但在我看来,可以说“包含由预处理器处理”。当编译器处理“预处理器步骤”时,行会变得模糊。这肯定不是链接器造成的问题-编译器将放弃很久之前,你得到的步骤

一般来说,将一个
.c
文件包含在另一个
.c
文件中是不好的做法-这是链接器的用途。为了防止“递归包含”,您经常会在编译器附带的
.h
文件中看到这样的结构:

#ifndef __MYINCLUDEFILE
#define __MYINCLUDEFILE
... put the body of the include file here
#endif
这样可以确保包含文件只包含一次,即使它是从多个位置调用的(第一次包含时,将定义
\uuu MYINCLUDEFILE
变量;下次包含时,将跳过整个函数体)。一旦对每个包含文件执行此操作,您陷入的那种“递归陷阱”就不会再发生


正如@wildplasser所指出的,
\u NAME
\u NAME
的使用是为语言和实现保留的-我将其用作示例,因为您将在编译器附带的头文件中看到类似的构造。当您创建自己的.h文件时,您必须考虑另一种创建唯一标识符的约定。

代码更改有不同的阶段


在预处理阶段扩展了include。因此,当您尝试进行无限循环时,实际上只有在编译或链接发生之前,才在预处理阶段出错。

以“#”开头的所有“内容”都由预处理器处理。这就是为什么include-guard也以“#”开头。它必须同时处理。

预处理已经失败了。要演示,请使用

gcc -E try.c

然后看到它失败。

这就是.h文件的用途。我们通常不包含.c文件。我知道。但是您也可以包含.c文件,我不想在这个消息中添加4个文件(2.h和2.c)。只是想让事情变得更简短和简单。预处理器在几年前在GCC中集成到编译器中(尽管它仍然可以作为单独的阶段显式调用)。正如C标准中所定义的,预处理是翻译的一部分。C实现可以单独实现预处理,也可以与翻译的其余部分统一实现预处理。@EricPostphil-我想这是一个语义问题。“编译过程”(全部由gcc处理)包括许多步骤:预处理、编译、组装、链接和加载。用一个步骤调用它的事实并没有消除存在多个阶段的概念。我相信在“编译”第一行代码之前,所有的
#
指令都会被解释/扩展等,这是一个事实。示例(非权威)链接:。你有反例吗?@Floris:带有一个或两个前导下划线的预处理器符号是为语言和实现保留的。最好避免它们。@Floris:这是准确的,但省略预处理和编译的讨论可能更清楚,因为它与问题并不真正相关。无论何时进行预处理,递归包含文件(无条件限制)都是错误的。@Floris:IMHO事件顺序未定义。(显然,在预处理器发出标记之前,编译器无法看到它)。该规则可能应该被视为一个
规则,就像
规则一样:令牌流不应该与独立预处理器过程生成的令牌流不同。“流水线”实现(如GCC)实际上可以在预处理器在所有(include)文件上看到EOF之前将令牌馈送到编译器中。第一个错误时中止可能会中止管道,而不允许它读取EOF。没必要那样。
gcc -E try.c