函数原型

函数声明位于函数定义之前,用来指定函数的名称、返回类型、存储类和其他特性。 若要作为原型,函数声明还必须为函数的参数确定类型和标识符。

语法

declaration
declaration-specifiers attribute-seqopt init-declarator-listopt ;

/* attribute-seqopt 为 Microsoft 专用 */

declaration-specifiers
storage-class-specifier declaration-specifiersopt
type-specifier declaration-specifiersopt
type-qualifier declaration-specifiersopt

init-declarator-list
init-declarator
init-declarator-list , init-declarator

init-declarator
declarator
declarator = initializer

declarator
pointeropt direct-declarator

direct-declarator:/* 函数声明符 */
direct-declarator ( parameter-type-list ) /* 新样式声明符 */
direct-declarator ( identifier-listopt ) /* 弃用样式声明符 */

原型与函数定义具有相同的形式,只不过前者由紧跟在右括号后的分号结尾,因此没有主体。 在任一情况下,返回类型都必须与函数定义中指定的返回类型一致。

函数原型有下列重要用途:

  • 它们建立返回除 int 之外的类型的函数的返回类型。 尽管返回 int 值的函数不需要原型,但仍建议使用原型。

  • 如果没有完整原型,将进行标准转换,但不会尝试使用形参的数量检查实参的类型或数量。

  • 原型用于在定义函数之前初始化指向函数的指针。

  • 形参列表用于检查函数调用中的实参是否与函数定义中的形参匹配。

每个形参的转换类型决定函数调用在堆栈上放置的实参的解释。 自变量和参数的类型不匹配可能导致堆栈上的自变量被错误解释。 例如,在 16 位计算机上,如果 16 位指针先作为实参传递,再声明为 long 形参,那么堆栈上的前 32 位将解释为 long 形参。 此错误不仅会导致 long 参数出现问题,而且还会导致所有后续参数都出现问题。 您可通过声明所有函数的完整函数原型来检测此类错误。

原型将确定函数的属性。 随后将检查位于函数定义前面(或者出现在其他源文件中)的函数调用是否存在参数类型和返回类型不匹配项。 例如,如果在原型中指定 static 存储类说明符,还必须在函数定义中指定 static 存储类。

完整参数声明 (int a) 可以与同一声明中的抽象声明符 (int) 组合在一起。 例如,以下声明是合法的:

int add( int a, int );

原型可以同时包含作为参数传递的每个表达式的类型和标识符。 但是,此类标识符的范围只到该声明的末尾。 原型也可以反映参数的数量是变量或未传递参数的事实。 如果没有此类列表,则可能不会显示不匹配项,从而使编译器无法生成有关它们的诊断消息。 有关类型检查的详细信息,请参阅参数

使用 /Za 编译器选项进行编译时,Microsoft C 编译器中的原型范围现在符合 ANSI 一致性。 如果你在原型中声明 structunion 标记,那么标记是在此范围(而不是全局范围)输入的。 例如,为符合 ANSI 一致性而使用 /Za 进行编译时,绝不可能在调用此函数时不会遇到类型不匹配错误:

void func1( struct S * );

若要更正代码,请在函数原型之前在全局范围定义或声明 structunion

struct S;
void func1( struct S * );

/Ze 下,仍将在全局范围内输入标记。

请参阅

函数