函数原型
函数声明位于函数定义之前,用来指定函数的名称、返回类型、存储类和其他特性。 若要作为原型,函数声明还必须为函数的参数确定类型和标识符。
语法
declaration
?
declaration-specifiers
attribute-seq
opt init-declarator-list
opt ;
/* attribute-seq
opt 为 Microsoft 专用 */
declaration-specifiers
?
storage-class-specifier
declaration-specifiers
opt
type-specifier
declaration-specifiers
opt
type-qualifier
declaration-specifiers
opt
init-declarator-list
?
init-declarator
init-declarator-list
,
init-declarator
init-declarator
?
declarator
declarator
=
initializer
declarator
?
pointer
opt direct-declarator
direct-declarator
:/* 函数声明符 */
direct-declarator
(
parameter-type-list
)
/* 新样式声明符 */
direct-declarator
(
identifier-list
opt )
/* 弃用样式声明符 */
原型与函数定义具有相同的形式,只不过前者由紧跟在右括号后的分号结尾,因此没有主体。 在任一情况下,返回类型都必须与函数定义中指定的返回类型一致。
函数原型有下列重要用途:
它们建立返回除
int
之外的类型的函数的返回类型。 尽管返回int
值的函数不需要原型,但仍建议使用原型。如果没有完整原型,将进行标准转换,但不会尝试使用形参的数量检查实参的类型或数量。
原型用于在定义函数之前初始化指向函数的指针。
形参列表用于检查函数调用中的实参是否与函数定义中的形参匹配。
每个形参的转换类型决定函数调用在堆栈上放置的实参的解释。 自变量和参数的类型不匹配可能导致堆栈上的自变量被错误解释。 例如,在 16 位计算机上,如果 16 位指针先作为实参传递,再声明为 long
形参,那么堆栈上的前 32 位将解释为 long
形参。 此错误不仅会导致 long
参数出现问题,而且还会导致所有后续参数都出现问题。 您可通过声明所有函数的完整函数原型来检测此类错误。
原型将确定函数的属性。 随后将检查位于函数定义前面(或者出现在其他源文件中)的函数调用是否存在参数类型和返回类型不匹配项。 例如,如果在原型中指定 static
存储类说明符,还必须在函数定义中指定 static
存储类。
完整参数声明 (int a
) 可以与同一声明中的抽象声明符 (int
) 组合在一起。 例如,以下声明是合法的:
int add( int a, int );
原型可以同时包含作为参数传递的每个表达式的类型和标识符。 但是,此类标识符的范围只到该声明的末尾。 原型也可以反映参数的数量是变量或未传递参数的事实。 如果没有此类列表,则可能不会显示不匹配项,从而使编译器无法生成有关它们的诊断消息。 有关类型检查的详细信息,请参阅参数。
使用 /Za
编译器选项进行编译时,Microsoft C 编译器中的原型范围现在符合 ANSI 一致性。 如果你在原型中声明 struct
或 union
标记,那么标记是在此范围(而不是全局范围)输入的。 例如,为符合 ANSI 一致性而使用 /Za
进行编译时,绝不可能在调用此函数时不会遇到类型不匹配错误:
void func1( struct S * );
若要更正代码,请在函数原型之前在全局范围定义或声明 struct
或 union
:
struct S;
void func1( struct S * );
在 /Ze
下,仍将在全局范围内输入标记。