为什么编译器只向后看类型和函数声明?

时间:2014-04-21 22:28:13

标签: c++ c parsing

这纯粹是为了满足我自己的好奇心,但为什么函数和类型只针对代码中定义的早期解决,而不是同一范围内的 where

有时它会在函数需要相互调用时出现:

void foo(int depth) { bar(depth + 2); }
void bar(int depth) { if (depth > 0) foo(depth - 3); }

你需要在foo之前移动bar,或者事先声明bar:

void bar(int);
void foo(int depth) { bar(depth + 2); }
void bar(int depth) { if (depth > 0) foo(depth - 3); }

在更复杂的示例中,您可能需要查看#include树和#ifndef警卫,以找出您的代码无法识别其他文件中定义的函数或类型的原因,如果您已经了解了#39;应该检查一下。


然后当然有这个经典之作:

typedef struct {
    Node *next;
} Node;

您需要知道这是可能的:

typedef struct Node {
    struct Node *next;
} Node;

......但很明显很多人不这样做,所以他们只是使用“无效”。或者' struct Node'无处不在。


那么,为什么这些编译器也不能解决这个问题呢?我可以理解预处理器只是向后检查,因为事情可以是#undefined,但是一旦声明了类型或函数它就会好,并且不能被后面的定义重载。

是否出于历史原因,首次开发时可用的技术有限?或者是否有一些我错过的逻辑歧义?

3 个答案:

答案 0 :(得分:4)

你的问题的答案很简单,“如果没有编写编译器就会变得更难” - 语言规范说它必须是这样的,但是语言中这种措辞的原因规范是“它使以这种方式编写编译器变得更容易”。在某种程度上,也许在过去,编译器会“按照它们的方式”生成代码,而不是首先读取翻译单元(源文件)中的所有代码,然后处理它。

请记住,C和C ++编译器仍然在没有大量内存和非常快的处理器的机器上使用。因此,如果您尝试在小容量计算机上编译大量代码,那么“我们不想先读取所有源代码,然后将其删除”方法比16GB四核桌面更有意义机。我希望您可以将一个相当大的项目的整个源代码一次性加载到内存中(例如,LLVM + Clang中的所有文件大约为450MB,因此可以轻松地放入现代桌面/笔记本电脑的内存中)。

编辑:应该注意的是,“解释型语言”,例如PHP,JavaScript和Basic,通常没有这个要求,但其他编译语言通常也有 - 例如Pascal有一个特殊的关键字forward告诉编译器这个函数存在,但我告诉你它后面包含什么。

Pascal和C(以及C ++,因为它在这方面基于C)允许指向尚未完成的结构。只是这个简单的“你还没有所有的信息”意味着编译器必须建立类型信息,然后“返回并修复它”[显然只在需要时]。但它允许我们这样做:

struct X
{
   struct X* next;
   ... 
};

或在C ++中:

struct X
{
   X* next; 
   ...
};

Edit2:GCC开发人员Jan Hubicka撰写的这篇博客解释了“立即处理所有代码”的一些问题。当然,我们大多数人都没有编译Firefox和类似大小的项目,但是大型项目,当你必须同时处理所有代码时,即使使用现代机器,也会导致“内存不足”的问题不要“不时地让编译器节食”。

http://hubicka.blogspot.co.uk/2014/04/linktime-optimization-in-gcc-1-brief.html

答案 1 :(得分:3)

原因是在需要时存在的实体的所有必要信息允许编译器在单次传递中翻译源文件。参看http://blogs.msdn.com/b/ericlippert/archive/2010/02/04/how-many-passes.aspx,用于与C#进行比较(它不需要先前的声明;但无论如何一切都在一个类中。)

C与Pascal和其他一些语言共享此功能。编写这样的编译器更容易,并且正如其他人所指出的那样,往往会使用更少的内存(但可能会增加编译时间,相反,因为标头中的声明正在为每个转换单元反复解析/编译)。

答案 2 :(得分:0)

因为任何类型的逻辑演绎都需要某种“逻辑顺序”。

你故意忽略事情的顺序,在这里表达你的观点。好的,很好,但这是非常无知的。

在C和C ++中,您有一个尝试解决一些典型问题的命令:声明和定义。在C / C ++中,您可以引用至少声明的内容,即使它们尚未定义。

但是,这种分离或“宣告”和“定义”只是对逻辑顺序的放宽,最终---只是对事物的简化。

想象一个纯粹的简单语言表达式序列来描述你的程序(与任何编程语言相反,它试图表达相同的,但甚至试图“编译”成一些实际的计算机程序):A是B,但是B取决于C,但B只有C才是C,只有当A是B时D可能是什么C可能是什么C可能是A什么D是C。

WTF ???

如果你可以推断一个真实世界的解决方案来解决一个不依赖于任何逻辑顺序的专用现实世界问题,那么你就会知道你的答案,并且只要知道它就可能变得非常富有。