【问题标题】:What are prototypes in programming?什么是编程原型?
【发布时间】:2020-12-03 17:31:56
【问题描述】:

这不是关于语言的具体问题,而是关于一般编程的更多问题。这个问题是在我与一位朋友就我最近从我的 C++ 课程中了解到的“函数原型”开始争论之后提出的。我向他提到原型是您必须在代码开头创建的函数头,因此编译器会在运行时分配一些空间,然后才能到达实际函数。然后我们开始讨论其他不使用函数原型的编程语言(如 java 或 python)——就我们而言——实际上是否有一个类似于 C++ 的系统,只是它们自己处理而不是处理它让用户创建它们。

所以我们很想知道,函数原型到底是什么?他们只对 C/C++ 负责,还是其他编程语言使用它们?作为未来的程序员,我需要更多地开发它吗?感谢您的帮助!

【问题讨论】:

    标签: language-agnostic function-prototypes


    【解决方案1】:

    对于 C 和 C++,“原型”一词指的是特定的声明语法。

    在 C 的最早版本中,函数定义写成

    int func( arg1, arg2, arg3 ) // no types in argument list, just identifiers
      int arg1;
      double arg2;
      char *arg3;
    {
      // function body
    }
    

    声明写成

    int func( ); // no argument list
    

    函数参数列表只包含标识符,没有类型信息——这是单独提供的。函数声明不包含参数,只包含返回类型。

    C++引入,C后来采用了原型语法的概念,其中类型信息都包含在定义中的参数列表中:

    int func( int arg1, double arg2, char *arg3 )
    {
      // function body
    }
    

    和声明

    int func( int, double, char * );
    

    这允许编译器检查函数调用中参数的数量和类型,并在它们不匹配时发出诊断,而不是等到运行时才发现是否存在问题。 p>

    虽然仍然支持旧式函数声明和定义语法,但它不应该用于新代码开发——我们几乎到了“原型”这个词有点多余的地步,因为原型语法是规范而不是例外。

    Fortran、Pascal 和 Ada 等静态类型语言都有单独的函数声明,但它们并不将这些声明称为原型。同样对于 C,“原型”指的是函数声明和定义的特定样式,而不仅仅是声明本身。

    【讨论】:

      【解决方案2】:

      这大大简化了,但函数原型的原因是“一次性编译器”。

      如果您的编译器只对代码进行一次传递,则它需要在调用函数实现之前知道哪些函数可供它使用。这就是原型的用武之地。

      编译器多次遍历代码构建跳转表,告诉它所有函数在哪里,因此不需要函数原型。


      在 C 编译器中,原型用于检查函数参数的类型和数量(即函数签名)。这个程序:

      #include <stdio.h>
      
      int main()
      {
          printf("%d\n",add(3));
      }
      
      int add(int i, int j)
      {
          return i+j;
      }
      

      compiles and executes in Clang 7 with a warning,即使结果毫无意义(即未定义的行为)。

      而这个程序,包含一个函数原型:

      #include <stdio.h>
      
      int add (int, int); /* function prototype for add */
      
      void main()
      {
          printf("%d\n",add(3));
      }
      
      int add(int i, int j)
      {
          return i+j;
      }
      

      fails to compile.

      【讨论】:

      • 如果我错了,请纠正我,但您所描述的更多的是函数 声明 的特征,而不是原型。至少在 C 中,所谓的“原型”只是一个函数名/返回,没有已知的签名(void foo() 是一个原型,但void foo(void) 是一个声明)。像 C++ 这样的语言甚至没有原型(至少在 C 定义中没有);它只有声明。
      • @Human-Compiler 我认为这个术语在不同的环境中可能会有所不同。例如,维基百科不同意您:en.wikipedia.org/wiki/Function_prototype
      • 在仔细检查了 C 标准之后,我在上面的陈述中似乎是错误的(我的概念被颠倒了)。 C 引用原型来表示函数声明带有参数列表,而函数声明器包含一个空列表(尽管这是一个遗留/过时的特性)。抱歉!
      【解决方案3】:

      C 和 C++ 被编译为本机代码并支持编译单元(文件)之间的调用。为了从相邻的编译单元调用函数 XYZ,编译器插入一个引用“调用 XYZ”,稍后由链接器解析。但是您需要知道在堆栈上为函数准备什么。原型无需编译整个函数即可提供该信息。

      早期 C 将所有内容都视为 int,并且由于 C 调用约定是 caller-cleans-up,因此作为调用者的您知道在函数返回后要从堆栈中删除多少 int。如果你用三个参数调用printf 而不解释它是什么,C 编译器仍然可以确定要生成什么代码。如果您将其拼错为vrintf,它将编译但无法链接。所以普通的 C 在某种程度上可以在没有原型的情况下工作(仍然有效?)(或将丢失的原型视为警告)。

      由于 C++ 可以将各种疯狂的东西作为函数参数传递,如果你试图在没有先解释其参数类型的情况下调用某个东西,编译器不知道要生成什么代码,你会得到一个错误。

      这篇外部文章相当不错:http://www.cplusplus.com/articles/yAqpX9L8/

      【讨论】:

      • 那篇文章真的很有帮助!非常感谢!
      猜你喜欢
      • 2011-11-25
      • 1970-01-01
      • 2013-02-09
      • 1970-01-01
      • 1970-01-01
      • 2012-03-22
      • 2019-07-27
      • 2012-05-18
      • 1970-01-01
      相关资源
      最近更新 更多