这纯粹是为了满足我自己的好奇心,但为什么函数和类型只能在代码中先前定义的那些中解析,而不是在同一范围内的任何地方?
有时它会在函数需要相互调用时显示:
void foo(int depth) { bar(depth + 2); }
void bar(int depth) { if (depth > 0) foo(depth - 3); }
Run Code Online (Sandbox Code Playgroud)
你需要在foo之前移动bar,或者事先声明bar:
void bar(int);
void foo(int depth) { bar(depth + 2); }
void bar(int depth) { if (depth > 0) foo(depth - 3); }
Run Code Online (Sandbox Code Playgroud)
在更复杂的示例中,您可能需要查看#include树和#ifndef警卫,以找出您的代码无法识别某个其他文件中定义的函数或类型的原因,如果您已经知道您应该检查该代码.
然后当然有这个经典:
typedef struct {
Node *next;
} Node;
Run Code Online (Sandbox Code Playgroud)
你需要知道这是可能的:
typedef struct Node {
struct Node *next;
} Node;
Run Code Online (Sandbox Code Playgroud)
......但显然许多人不这样做,他们只是到处都使用'void*'或'struct Node'.
那么,这些编译器也没有解决这个问题的原因吗?我可以理解预处理器只是向后检查,因为事情可以是#undefined,但是一旦声明了类型或函数,它就会存在并且不能被后来的定义重载.
这是出于历史原因,它们在首次开发时可用的技术有限吗?或者是否存在一些我遗漏的逻辑歧义?
你的问题的答案很简单,“如果不这样做,编写编译器就会变得更加困难”——语言规范说它必须如此,但语言规范中这种措辞的原因是“这样可以更轻松地编写编译器”。在某种程度上,也可能是在过去,编译器会“随着进展”生成代码,而不是首先读取翻译单元(源文件)中的所有代码,然后对其进行处理。
请记住,C 和 C++ 编译器仍在没有大量内存和非常快的处理器的机器上使用。因此,如果您尝试在小容量计算机上编译大量代码,那么“我们不想先阅读所有源代码,然后对其进行修改”的方法比在 16GB 四核台式机上更有意义机器。我希望您可以将一个相当大的项目的整个源代码一次性加载到内存中(例如,LLVM + Clang 中的所有文件约为 450MB,因此可以轻松放入现代台式机/笔记本电脑的内存中)。
编辑:应该注意的是,“解释语言”,例如 PHP、JavaScript 和 Basic,通常没有此要求,但其他编译语言通常有 - 例如 Pascal 有一个特殊的关键字来forward告诉编译器此函数存在,但我稍后会告诉你它包含什么。
Pascal 和 C(以及 C++,因为它在这方面基于 C)都允许指向尚未完成的结构的指针。只是这个简单的“您还没有获得所有信息”意味着编译器必须构建类型信息,然后“返回并修复它”[显然仅根据需要]。但它允许我们做:
struct X
{
struct X* next;
...
};
Run Code Online (Sandbox Code Playgroud)
或在 C++ 中:
struct X
{
X* next;
...
};
Run Code Online (Sandbox Code Playgroud)
Edit2:GCC 开发人员 Jan Hubicka 的博客解释了“一次处理所有代码”的一些问题。当然,我们大多数人都不会编译 Firefox 和类似大小的项目,但是大型项目,当您必须一次处理所有代码时,即使使用现代机器,如果开发人员也确实会导致“可用内存不足”的问题不要“时不时地让编译器节食”。
http://hubicka.blogspot.co.uk/2014/04/linktime-optimization-in-gcc-1-brief.html
| 归档时间: |
|
| 查看次数: |
214 次 |
| 最近记录: |