编译器从哪里开始读取?

3
这是一个小程序:
#include <iostream>
using namespace std;

int main() {
    f();
    system("pause");
}

void f() {
    static int x = 20 ;
    class tester {
    public :
        tester() {
            cout << x ;
        }
    } x1;
}

我遇到了这个错误:错误 C3861:未找到标识符"f"

如果我把函数f放在main函数上面,我就能得到想要的输出结果。

为什么会这样呢?我被告知程序执行从main函数开始。按照这个逻辑,第一种情况下代码也应该运行。

编译器是如何开始读取程序的?


2
从上到下,你的f()在最底部,当然它看不见它。 - Adrian
@vBx 你说得好像所有的语言在引用之前都需要定义一样。试着用Python写一下这个,看看会发生什么。 - Michael Mrozek
6个回答

7

我被告知程序的执行从主函数开始。

这正是关键所在。

编译器从 main 开始,然后看到对 f() 的调用,它之前没有遇到过(因为它是在后面定义的),所以不知道该怎么处理。

如果你想在 main 之后定义 f,可以在其前面放置一个函数原型,例如

#include <iostream>
using namespace std;

void f(); // <--- This tells the compiler that a function name f will be defined

int main() {
f();
system("pause");
}

void f() {
static int x = 20 ;
class tester {
public :
    tester() {
        cout << x ;
    }
} x1;
}

7

程序编译和执行的开始是两个不同的阶段。

执行从main函数开始。

编译从文件的开头开始,编译器不会“跳来跳去”查找所需的代码片段,而是按线性顺序读取输入(我猜这与C++语法非常复杂有关)。

当编译器在解析文件的某个点时,它只知道到该点为止已经声明或定义了什么1

因此,发明了函数原型(以及一般的非定义声明):在文件开头通常放置所有在文件中定义的函数的原型,通常在#include指令之后或在一个单独的包含文件中。原型告诉编译器这些函数将稍后被定义,并指定函数签名(即名称、参数、返回值)。

原型与普通函数相同,但没有函数体,用分号代替2。例如,在您的代码中,可以编写如下原型:

void f();

main函数之前。


  1. IIRC(如果我没记错)有一些放宽规定的情况,允许编译器“等待”一些声明来实现某些模板魔法,但这与本题无关。

  2. 在原型中,通常也不写参数的名称,只留下它们的类型(在函数定义中也可以这样做,但除非你有一个不使用的形式参数,否则在那里没有太多意义)。尽管如此,我还是喜欢将参数名称作为文档留在那里。


你的第二点有些误导性;在函数定义中,你同样可以省略参数的名称。显然,你不能引用一个没有名字的参数,因此也不能使用它的值。 - MSalters
@MSalters:不知道那个,我会修复答案。 - Matteo Italia

3
为了能够调用函数,它必须在代码中的早期阶段进行声明。这只是语言规则的一部分,旨在帮助编译器。
您可以使用以下方法在较早的位置进行函数声明:
void f();

然后像您所做的那样,在主函数之后定义它。


3
编译器从顶部开始读取并向下阅读。您需要类似以下的内容:
#include <iostream>
using namespace std;

void f();

int main() {
f();
system("pause");
}

void f() {
static int x = 20 ;
class tester {
public :
    tester() {
        cout << x ;
    }
} x1;
}

2
在编译过程中,当编译器在评估 main() 时,需要提前知道 f() 是什么,才能生成正确的汇编代码来调用该函数。这就是为什么在这种情况下需要将其放置在 main() 前面的原因。
作为替代方案,您可以在 main() 之前声明 f() 的原型,以便编译器知道它是一个本地函数,在您的文件的其他位置声明:
void f(); // prototype

int main() 
{
  // .. code ..
}

void f() // implementation of f()
{
 // .. code ..
}

2
不,编译器必须在使用之前至少看到f()的声明。C++代码文件是一个简单的文本文件,必须由编译器从开头到结尾进行读取。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接