C/C++原型的目的

pyt*_*bie 25 c c++ compiler-construction

我正在阅读关于C/C++ Prototype语句的维基百科,我很困惑:

维基百科说:"通过包含函数原型,您可以通知编译器函数"fac"采用一个整数参数,并使编译器能够捕获这些类型的错误."

并使用以下作为示例:

#include <stdio.h>

 /* 
  * If this prototype is provided, the compiler will catch the error 
  * in main(). If it is omitted, then the error will go unnoticed.
  */
 int fac(int n);              /* Prototype */

 int main(void) {             /* Calling function */
     printf("%d\n", fac());   /* ERROR: fac is missing an argument! */
     return 0;
 }

 int fac(int n) {             /* Called function  */
     if (n == 0) 
         return 1;
     else 
         return n * fac(n - 1);
}
Run Code Online (Sandbox Code Playgroud)

但是被调用函数的函数定义已经包含了原型告诉编译器的所有信息,那么为什么编译器不能从被调用函数的定义中推导出这些信息,因为它们包含相同的字母/信息字母?

我错过了什么?似乎是额外的工作没有明显的收获.

编辑:谢谢你们.我猜想编译器是多遍的.我被宠坏了像Python这样的当前语言.这是有道理的,因为它需要一些kludges才能在一次通过中准确地完成任务.现在对我来说似乎更加明显.显然,它需要对编译器如何链接和编译有相当深入的了解.

Tyl*_*nry 26

两个原因:

  1. 编译器从上到下读取文件.如果facmain上面使用了if fac,并且没有原型,则编译器不知道如何检查该调用是否正确完成,因为它尚未达到定义fac.

  2. 可以将C或C++程序拆分为多个文件.fac可以在与编译器当前正在处理的文件完全不同的文件中定义,因此它需要知道该函数存在于某个地方,以及它应该如何被调用.

请注意,您发布的示例中的注释仅适用于C.在C++中,该示例将始终产生错误,即使省略原型(尽管根据原型是否存在会产生不同的错误).在C++中,所有函数都需要在使用之前定义或原型化.

在C中,您可以省略原型,编译器将允许您使用任意数量的参数(包括零)调用该函数,并将假定返回类型为int.但仅仅因为它在编译期间没有对你大喊大叫并不意味着如果你不以正确的方式调用函数,程序将正常工作.这就是为什么它在C中原型是有用的:所以编译器可以代表你仔细检查.

推动这种功能的C和C++背后的哲学是这些是相对低级的语言.他们不会进行大量的手持操作,如果进行任何运行时检查,他们也不会做太多.如果你的程序做错了什么,它会崩溃或表现得很奇怪.因此,语言包括像这样使编译器在编译时识别某些类型的错误,这样就可以更方便地找到和解决这些问题的功能.

  • @pythonnewbie:现在很容易解决这两个(以及更多)问题,但请记住,C可以追溯到70年代.当然,语言可能需要一个多遍编译器,它可以访问所有使用过的`.cpp`文件(而不是只包含原型的头文件和带有实现的已编译目标文件),因此它可以推断出函数签名和所有.但那时候那个非常昂贵,所以他们没有. (2认同)

Dre*_*all 15

原型允许您将接口与实现分开.

在您的示例中,所有代码都存在于一个文件中,您可以轻松地将fac()定义移动到原型当前所在的位置并移除原型.

真实世界的程序由多个.cpp文件(也称为编译单元)组成,经常编译并链接到库中,然后链接到最终的可执行形式.对于那种性质的大型项目,原型被收集到.h文件(也称为头文件)中,其中头在编译时包含在其他编译单元中,以警告编译器存在和调用库中的功能约定.在这些情况下,函数定义不可用于编译器,因此原型(也称为声明)用作定义库的功能和要求的一种契约.


Bre*_*ias 5

原型的最重要原因是解决循环依赖.如果"main"可以调用"fac","fac"调用"main",那么你将需要一个原型来解决这个问题.