ANSI C函数声明如何改进旧的Kernigan和Ritchie风格?

关于ANSI C函数声明,这是如何从旧的K&R风格改进的? 我知道它们之间的区别,我只想知道使用旧样式会出现什么问题以及新样式是如何改进的。

特别是旧式函数声明不允许对调用进行编译时检查。

例如:

int func(x, y) char *x; double y; { /* ... */ } 

 func(10, 20); 

当编译器看到调用时,它不知道函数func的参数类型,因此无法诊断错误。

相比之下:

 int better_func(char *x, double y) { /* ... */ } 

 better_func(10, 20); 

将导致编译器错误消息(或至少是警告)。

另一个改进:原型使得函数可以使用float类型的参数,以及比int更窄的整数类型(3个char类型和两个short类型)。 如果没有原型, float会被提升为double ,而窄整数类型会被提升为intunsigned int 。 使用原型, float参数作为float传递(除非函数是可变参数,如printf ,在这种情况下旧规则适用于可变参数)。

C Rationale文档在第6.7.5.3节中对此进行了讨论,可能比我更好:

函数原型机制是C语言最有用的补充之一。 当然,这一特征在过去25年来的许多Algol派生语言中都有先例。 标准中采用的特定forms在很大程度上基于C ++。

函数原型提供强大的转换时错误检测function。 在没有原型的传统C实践中,转换器在调用另一个源文件中声明的函数时很难检测错误(错误的数量或类型的参数)。 在运行时或通过使用辅助软件工具检测到此类错误。

在不在函数原型范围内的函数调用中,整数参数应用了整数提升,并且浮点参数被加宽为double 。 在这样的调用中不可能传递未转换的charfloat参数。 函数原型为程序员提供了对函数参数类型转换的显式控制,因此实现可以抑制通常不合适且有时效率低下的参数默认扩展规则。

还有更多; 去读它。

K&R中的非定义函数声明如下所示

 int foo(); 

并引入了一个接受未指定数量的参数的函数。 这种声明风格的问题很明显:它既没有指定参数的数量,也没有指定它们的类型。 编译器无法根据调用点处的参数数量或类型来检查调用的正确性。 在参数类型与预期参数类型不匹配的情况下,编译器无法执行参数类型转换或发出错误消息。

函数声明用作K&R中函数定义的一部分,如下所示

 int foo(a, b) int a; char b; { ... 

它指定参数的数量,但仍未指定其类型。 此外,即使参数的数量似乎通过此声明公开,它仍然正式声明fooint foo(); 这意味着将其称为foo(1, 2, 3, 4, 5)仍然不构成约束违规。

新样式,即原型声明更好,原因很明显:它暴露了参数的数量和类型。 它强制编译器检查调用的有效性(关于参数的数量和类型)。 它允许编译器执行从参数类型到参数类型的隐式类型转换。

原型声明提供了其他不太明显的好处。 由于函数参数的数量和类型对于调用者和函数本身都是精确已知的,因此可以选择在调用时传递参数(调用约定)的最有效方法,而无需查看函数定义。 如果没有这些信息,K&R实施将被迫遵循所有function的单一预定“一刀切”调用约定。