函数原型

函数声明位于函数定义之前,并指定函数的名称、返回类型、存储类和其他属性。 要成为原型,函数声明还必须为函数的参数建立类型和标识符。

语法

declaration:
declaration-specifiers attribute-seq 选择init-declarator-list选择;

/* attribute-seq opt 是特定于Microsoft */

declaration-specifiers:
storage-class-specifier declaration-specifiers 选择
type-specifier declaration-specifiers 选择
type-qualifier declaration-specifiers 选择

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

init-declarator:
declarator
declarator = initializer

declarator:
pointer 选择direct-declarator

direct-declarator:/* 函数声明符 */
direct-declarator ( parameter-type-list ) /* 新样式声明符 */
direct-declarator ( identifier-list 选择) /* 过时样式声明符 */

原型与函数定义具有相同的形式,只是在右括号后立即以分号终止,因此没有正文。 在任一情况下,返回类型都必须与函数定义中指定的返回类型一致。

函数原型具有以下重要用途:

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

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

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

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

每个参数的转换类型决定了函数调用在堆栈上放置的参数的解释。 参数和参数之间的类型不匹配可能会导致堆栈上的参数被错误解释。 例如,在 16 位计算机上,如果 16 位指针作为参数传递,然后声明为 long 参数,则堆栈上的前 32 位解释为 long 参数。 此错误不仅会创建 long 参数的问题,还会产生所有后续参数的问题。 可以通过为所有函数声明完整的函数原型来检测此类错误。

原型建立函数的属性。 然后,可以检查函数定义之前的函数调用(或在其他源文件中发生)是否存在参数类型和返回类型不匹配。 例如,如果在原型中指定 static 存储类说明符,则还必须在函数定义中指定 static 存储类。

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

int add( int a, int );

原型可以同时包含作为参数传递的每个表达式的类型和标识符。 但是,此类标识符仅在声明结束之前处于范围内。 原型还可以反映参数数可变或未传递任何参数的事实。 如果没有此类列表,可能无法显示不匹配,因此编译器无法生成有关它们的诊断消息。 有关类型检查的详细信息,请参阅 参数

使用编译器选项进行编译 /Za 时,Microsoft C 编译器中的原型范围现在符合 ANSI。 如果在原型中声明或structunion标记,则会在该范围而不是全局范围内输入标记。 例如,使用 /Za ANSI 一致性进行编译时,永远无法调用此函数,而不会收到类型不匹配错误:

void func1( struct S * );

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

struct S;
void func1( struct S * );

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

另请参阅

函数