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
,而窄整数类型会被提升为int
或unsigned int
。 使用原型, float
参数作为float
传递(除非函数是可变参数,如printf
,在这种情况下旧规则适用于可变参数)。
C Rationale文档在第6.7.5.3节中对此进行了讨论,可能比我更好:
函数原型机制是C语言最有用的补充之一。 当然,这一特征在过去25年来的许多Algol派生语言中都有先例。 标准中采用的特定forms在很大程度上基于C ++。
函数原型提供强大的转换时错误检测function。 在没有原型的传统C实践中,转换器在调用另一个源文件中声明的函数时很难检测错误(错误的数量或类型的参数)。 在运行时或通过使用辅助软件工具检测到此类错误。
在不在函数原型范围内的函数调用中,整数参数应用了整数提升,并且浮点参数被加宽为double 。 在这样的调用中不可能传递未转换的char或float参数。 函数原型为程序员提供了对函数参数类型转换的显式控制,因此实现可以抑制通常不合适且有时效率低下的参数默认扩展规则。
还有更多; 去读它。
K&R中的非定义函数声明如下所示
int foo();
并引入了一个接受未指定数量的参数的函数。 这种声明风格的问题很明显:它既没有指定参数的数量,也没有指定它们的类型。 编译器无法根据调用点处的参数数量或类型来检查调用的正确性。 在参数类型与预期参数类型不匹配的情况下,编译器无法执行参数类型转换或发出错误消息。
函数声明用作K&R中函数定义的一部分,如下所示
int foo(a, b) int a; char b; { ...
它指定参数的数量,但仍未指定其类型。 此外,即使参数的数量似乎通过此声明公开,它仍然正式声明foo
与int foo();
这意味着将其称为foo(1, 2, 3, 4, 5)
仍然不构成约束违规。
新样式,即原型声明更好,原因很明显:它暴露了参数的数量和类型。 它强制编译器检查调用的有效性(关于参数的数量和类型)。 它允许编译器执行从参数类型到参数类型的隐式类型转换。
原型声明提供了其他不太明显的好处。 由于函数参数的数量和类型对于调用者和函数本身都是精确已知的,因此可以选择在调用时传递参数(调用约定)的最有效方法,而无需查看函数定义。 如果没有这些信息,K&R实施将被迫遵循所有function的单一预定“一刀切”调用约定。