声明和原型的区别

Declaration and prototype difference

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

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

声明是标准中使用的通用术语,原型更具体。

引用 C11,章节 §6.7

A declaration specifies the interpretation and attributes of a set of identifiers. [...]

从 §6.7.6 开始,

Each declarator declares one identifier, and asserts that when an operand of the same form as the declarator appears in an expression, it designates a function or object with the scope, storage duration, and type indicated by the declaration specifiers.

另一方面,来自章节 §6.2.1

[....] A function prototype is a declaration of a function that declares the types of its parameters.

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


关于 "identifier":章节 §6.4.2.1,

An identifier is a sequence of nondigit characters (including the underscore _, the lowercase and uppercase Latin letters, and other characters) and digits, which designates one or more entities as described in 6.2.1. [...]

在第 6.2.1 章中,

An identifier can denote an object; a function; a tag or a member of a structure, union, or enumeration; a typedef name; a label name; a macro name; or a macro parameter. [....]

声明引入名称:

int a;    // "a" has type int

一个函数声明(不是原型)只引入了一个函数的名字和它的return类型:

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

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

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

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

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

  1. ...(A function prototype is a declaration of a function that declares the types of its parameters.)

这是一个提供函数参数类型信息的原型。

举个例子

void f();

void f( void );

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

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

  • 函数声明 是声明函数并以 ; 结尾的任何形式的行。

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

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

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