【问题标题】:Declaration and prototype difference声明和原型的区别
【发布时间】:2017-10-02 13:19:49
【问题描述】:

C 中的声明和原型有什么区别?在哪些情况下它们被称为声明以及在哪些原型中?

【问题讨论】:

  • 没有什么叫做变量原型。
  • 所有函数原型也是声明。但有些声明不是原型。具体来说,如果函数参数列表没有指定参数的类型(或void),那么它就不是原型。
  • @M.M 而不是“(或 void)”,我认为您的意思是说“(或空)”。 K&R 2Ed., A8.6.3 Function Declarators 指出:“在新式声明中,参数列表指定参数的类型。作为一种特殊情况,新式函数的声明器具有没有参数具有仅由关键字 void 组成的参数类型列表。”为了澄清“新风格”,第 202 页指出:“在新风格中,参数的类型是显式的,并且是函数类型的一部分;这样的声明也称为 函数原型。”
  • @ClintPachl 我的意思是“或无效”。但也许我的句子可以用两种方式解析。如果列表不是原型,则“或无效”属于列表不能做的事情。换句话说:如果列表既没有指定参数的类型,也不是(void),则它不是原型。
  • @M.M 你是对的。说“也不”有助于我理解我的误解。

标签: c language-lawyer declaration function-prototypes


【解决方案1】:

TL;DR;所有原型都是声明,但并非所有声明都是原型。

Declaration 是标准中使用的通用术语,prototype 更具体。

引用C11,第 6.7 章

声明指定一组标识符的解释和属性。 [...]

从 §6.7.6 开始,

每个声明器声明一个标识符,并断言当相同的操作数 当声明符出现在表达式中时,它用 声明说明符指示的范围、存储期限和类型。

另一方面,从第 §6.2.1 章开始

[....] 一个函数 原型是声明其参数类型的函数的声明。

所以说,原型是更完整的声明形式(包括参数的类型)。


关于“标识符”:第 §6.4.2.1 章,

标识符是一系列非数字字符(包括下划线_、 小写和大写拉丁字母和其他字符)和数字,指定 6.2.1 中描述的一个或多个实体。 [...]

在第 §6.2.1 章中,

一个标识符可以表示一个对象;一个函数;结构、联合的标签或成员,或 枚举;类型定义名称;标签名称;宏名;或宏参数。 [....]

【讨论】:

    【解决方案2】:

    一个声明引入了一个名字:

    int a;    // "a" has type int
    

    函数声明(也不是原型)只介绍函数的名称及其返回类型:

    int f();   // a function call expression "f(x, y, ...)" has type int
    

    但是,这样的函数声明并没有指定哪些参数对函数调用有效;换句话说,它没有指定函数signature。这是由函数原型(一种声明)完成的:

    int g1(void);         // "g1" takes no arguments, "g()" has type int
    int g2(float, char);  // "g2" takes two arguments
    int g3(int, ...);     // "g3" takes at least one argument
    

    函数原型在函数调用时是否可见对调用参数有重要影响:如果原型不可见,则所有参数都会进行默认参数提升。相比之下,如果原型可用,则函数参数将转换为相应形式参数的类型(如果参数数量不匹配或任何转换格式错误,则程序格式错误) .

    为了进一步探索这一点,请注意存在某些“不可能”的组合:如果我们有一个声明 int g2(); 和一个定义 int g2(float, char) { return 0; },则永远不可能仅使用声明调用 g2,因为形式参数类型不能由默认参数提升产生。通常建议始终使用原型声明,而永远不要使用非原型声明。

    最后,如果原型以省略号 (...) 结尾,则可以调用参数多于参数的原型函数。使用<stdarg.h> 获取这些参数要求函数原型在调用此类可变参数函数时可见。

    【讨论】:

    • 原型也是声明。
    • void h() {} 不是原型,例如int main() { if ( 0 ) main(5); } 是合法程序
    • @M.M:你确定吗?参考?
    • C11 6.9.7/1 “如果声明器包含参数类型列表,[...] 这样的声明器也可用作函数原型”。函数定义没有额外的案例。该定义确实“锁定”了在任何时候传递参数都是未定义的行为,但原型的诊断保证不存在
    • @M.M:好的,谢谢 - 我的错 :-S 我删除了有问题的声明。
    【解决方案3】:
    • 函数声明是声明函数并以; 结尾的任何形式的行。

    • prototype 是一个函数声明,其中指定了所有类型的参数。

    示例,原型函数声明:void func (void);
    示例,非原型函数声明:void func ();

    非原型函数声明是一个过时的功能 (6.11.6),可能会从 C 语言中删除。因此,您应该始终使用原型格式,而不是其他任何东西。

    【讨论】:

      【解决方案4】:

      根据 C 标准(6.2.1 标识符范围)

      1. ...(函数原型是声明函数的函数的声明 其参数的类型。)

      即原型提供了函数参数的类型信息。

      举个例子

      void f();
      

      void f( void );
      

      第一个声明不是原型,因为对函数参数一无所知。

      第二个声明是一个原型,因为它提供了函数参数的类型列表(它是一种特殊类型的类型列表,指定函数没有参数)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        相关资源
        最近更新 更多