为啥需要在定义或使用函数之前声明它?

Posted

技术标签:

【中文标题】为啥需要在定义或使用函数之前声明它?【英文标题】:Why does a function need to be declared before it's defined or used?为什么需要在定义或使用函数之前声明它? 【发布时间】:2010-02-07 19:44:14 【问题描述】:

在 C 中它是可选的。在 C++ 中,“必须” 在使用/定义函数之前声明一个函数。为什么会这样?有什么需要?我们不会在 C# 或 Java 中这样做。

有趣的是,当我们定义一个函数时。即使定义本身也有声明,我们也需要声明。天知道为什么?

【问题讨论】:

根据你的问题,我猜你不太喜欢 C++。 实际上,我爱上了 C/C++。然后我没有学习Java,我感觉很好。现在,我正在做 C#。我非常喜欢它,但现在我开始在 C/C++ 中查找错误。 :( 我们不应忘记,在 C++ 中,对于类 struct A void f() g(); /* before declared! */ void g() ;987654321@ 中的成员函数调用,此限制已解除。 给我看一个找到完美编程语言的程序员,我给你看一个对该语言没有完全理解的程序员。 不必定义之前声明函数。定义是完全有效的声明。当然,成员函数必须在类体内声明,但即便如此,它也可以同时声明和定义。 【参考方案1】:

有趣的是,就在本周,Eric Lippert 写了一篇与您的问题相关的博文:

http://blogs.msdn.com/ericlippert/archive/2010/02/04/how-many-passes.aspx

基本上,这与编译器的工作方式有关。 C# 和 Java 编译器通过了几次。如果他们遇到对未知方法的调用,这不是错误,因为稍后可能会找到定义,并且调用将在下一次传递时解决。请注意,我的解释过于简单,建议您阅读 Eric Lippert 的帖子以获得更完整的答案...

【讨论】:

【参考方案2】:

Java 和 C# 指定语言和二进制对象文件格式,它们是多遍编译器。

因此,他们能够查看后来的定义或单独编译的定义。

C 不能这样工作有几个原因:

如果不使用托管代码,则很难使用类型信息定义与机器无关的对象格式

C 故意允许绕过类型机制

最初定义时,通常没有足够的内存来运行复杂的编译器,也没有原型可供读取

C 程序必须具有任意大的系统特定库和搜索路径机制。所有这些都妨碍了定义基于对象模块的类型系统

C 可移植性和互操作性基础的一部分是规范的“仅输入语言”性质

直到最近,即使是 C 的有限的一次性特性,对于大型程序仍然几乎不实用。 Java 或 C# 之类的东西是不可能的:你可以休假,但你的 make(1) 仍然无法完成

【讨论】:

最后一点其实是make(1)的一个弱点,没有那么多C。它相当通用和强大,但代价非常大:make根本不明白它是什么正在做。通过了解 C 的基础知识,一个更专业的工具可以更有效地构建 C。 make 只是不了解标题。【参考方案3】:

基本上,这取决于您如何为该语言编写编译器。 在 C++ 中,决定使一次性编译成为可能。为此,您(或者更确切地说是编译器)需要能够首先阅读所有类、方法等的声明,然后阅读实现(或 C++ 术语中的定义)。在 Java 和 C# 中,编译器首先通读所有代码,生成与 C++ 编译器在读取头文件时生成的内容相对应的内容。然后 C#/Java 编译器读取实现(也称为定义)。因此,在 C++ 中,要求开发人员编写声明,而在 C# 中,编译器会多次运行代码,为开发人员完成声明工作。

顺便说一句,其他语言过去常常要求您按照需要的顺序编写函数(如果函数 B 使用函数 A,则必须先定义 A)。这些语言中的大多数都具有允许您解决此问题的结构。在 (Turbo) Pascal 中,解决方案在某种程度上与 C++ 中的相同。

【讨论】:

【参考方案4】:

C++ 与 Java/C# - 单遍编译器 (C++) 与多遍编译器(Java 和 C#)。多次传递允许 Java 和 C# 编译器查看未来的类型和函数原型。

C++ 与 C - 具有默认声明的 C 功能基本上是一个错误,已在 C++ 中修复。它会导致问题,并且是 gcc 的启用警告。在 C++ 中,参数构成函数导出名称的一部分(名称修改),因此在调用正确的函数之前必须知道。

【讨论】:

【参考方案5】:

在 C++ 中,“必须”在使用/定义函数之前声明一个函数。为什么会这样?有什么需要?我们不会在 C# 或 Java 中这样做。

我想说,这不是真的。是的,在 C++ 中,您必须在引用它之前定义一个函数签名(原型)。但是您可以将实现留到以后。

在 Java 中不起作用:如果没有编译该类(注意:与实现一起)并且在 javac 类路径中可用,则无法调用该类的方法。所以,Java 在这个意义上更加严格。

【讨论】:

我没有 -1 你,因为我认为 -2 就足够了。但是,您听说过接口吗?在 OOP 中,它们与您可以获得的函数原型一样接近。 关于java的部分根本不是真的。 Java 规范说:“在不同编译单元中声明的类型可以循环地相互依赖。Java 编译器必须安排同时编译所有这些类型”(7.3) 关于 C++ 的部分也不是真的。问题中的人是对的。

以上是关于为啥需要在定义或使用函数之前声明它?的主要内容,如果未能解决你的问题,请参考以下文章

函数原型函数声明和函数定义之间关系

Go教程函数

Go教程函数

为啥在某些语言中使用变量之前需要声明变量,而在其他语言中则不需要? [关闭]

为啥不包含头文件却可以调用函数,谁来解释

c#里为啥有的使用时函数需要new一个对象而有的不用?为啥不直接调用就好?