函数声明位于函数定义之前,并指定函数的名称、返回类型、存储类和其他属性。 要成为原型,函数声明还必须为函数的参数建立类型和标识符。
语法
              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,标记仍在全局范围内输入。