C++ C/C+的目的+;原型

C++ C/C+的目的+;原型,c++,c,compiler-construction,C++,C,Compiler Construction,我在读关于C/C++原型语句的维基百科,我很困惑: Wikipedia说:“通过包含函数原型,您通知编译器函数“fac”接受一个整数参数,并且您使编译器能够捕获这些类型的错误。” 并以以下为例: #include <stdio.h> /* * If this prototype is provided, the compiler will catch the error * in main(). If it is omitted, then the error wil

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

Wikipedia说:“通过包含函数原型,您通知编译器函数“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);
}
#包括
/* 
*如果提供了这个原型,编译器将捕获错误
*在main()中。如果省略了它,那么错误将不会被注意到。
*/
int fac(int n);/*原型*/
int main(void){/*调用函数*/
printf(“%d\n”,fac());/*错误:fac缺少参数*/
返回0;
}
int fac(int n){/*被调用函数*/
如果(n==0)
返回1;
其他的
返回n*fac(n-1);
}
但是被调用函数的函数定义已经包含原型告诉编译器的所有信息,那么为什么编译器不能从被调用函数的定义中推断出这些信息,因为它们包含完全相同的语句/信息

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


编辑:谢谢大家。我想我假设编译器是多通道的。我被当前的语言(如Python)宠坏了。这是有道理的,因为它太老了,需要一些乱七八糟的东西来在一次传递中准确地完成事情。现在对我来说似乎更明显了。显然,它需要非常熟悉编译器如何链接和编译。

C编译器从上到下处理源文件。解析参数类型时,不考虑使用后出现的函数。因此,在您的示例中,如果
main()

  • 编译器从上到下读取文件。如果
    fac
    fac
    上方的
    main
    中使用
    fac
    ,并且不存在原型,编译器不知道如何检查调用是否正确执行,因为它尚未达到
    fac
    的定义

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

    注意,C.所发布的示例中的注释只在C++中应用于该示例,即使原型被省略,该示例也会产生错误(尽管根据原型是否存在而产生不同的错误)。在C++中,所有函数都需要在使用前定义或原型化。p> 在C语言中,您可以省略原型,编译器将允许您使用任意数量的参数(包括零)调用函数,并将假定返回类型为

    int
    。但是,仅仅因为它在编译过程中不会对您大喊大叫,并不意味着如果您不以正确的方式调用该函数,程序就会正常工作。这就是为什么用C语言进行原型化很有用:这样编译器就可以代表您进行双重检查


    < C和C++背后的哲学是激励这些特征的,它们是相对低级的语言。他们不会做很多手牵手,也不会做很多运行时检查。如果你的程序做了一些不正确的事情,它将崩溃或行为怪异。因此,这些语言包含这样的功能,使编译器能够在编译时识别某些类型的错误,以便您可以更轻松地找到并修复它们。

    原型的最重要原因是解决循环依赖关系。如果“main”可以调用“fac”,而“fac”可以调用“main”,那么您将需要一个原型来解决这个问题。

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

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


    现实世界的程序由多个.cpp文件(又称编译单元)组成,在链接到最终可执行形式之前,经常编译并链接到库中。对于这种性质的大型项目,原型被收集到.h文件(也称为头文件)中,其中头文件在编译时包含在其他编译单元中,以提醒编译器库中存在和调用功能约定。在这些情况下,编译器无法使用函数定义,因此原型(又称声明)充当了一种合同,定义库的功能和要求。

    除了已经给出的所有好答案之外,请考虑一下:如果你是一名编译器,你的工作是用机器语言翻译源代码,而你(作为一个尽职尽责的编译器)只能逐行读取源代码——如果没有原型,你将如何读取粘贴的代码?您如何知道函数调用是有效的而不是语法错误?(是的,你可以做一个记录,并在最后检查所有内容是否匹配,但那是另一回事)

    另一种看待它的方式(这次是作为一个人):假设您没有定义为原型的函数,也没有它的源代码。但是,您知道,在您的伴侣提供给您的库中,有一个机器代码,当运行时,它会返回特定的预期行为。多好啊。现在,如果没有原型告诉编译器“嘿,伙计,相信我,有一个名为某某的函数,它接受参数并返回一些东西”,那么编译器怎么知道这样的函数调用是有效的呢

    我知道这是一种非常非常简单的思考方式
    #include <stdio.h>
    int main() {
       print( 5, "hi" );  // [1]
    }
    int print( int count, const char* txt ) {
       int i;
       for ( i = 0; i < count; ++i ) 
          printf( "%s\n", txt );
    }