关于 ANSI C 函数声明,这对旧的 K&R 风格有何改进?我知道它们之间的区别,我只想知道使用旧样式会出现什么问题以及新样式如何改进。
2 回答
特别是旧式函数声明不允许在编译时检查调用。
例如:
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
(3char
种类型和两种short
类型)更窄的参数。没有原型,float
被提升为double
,而窄整数类型被提升为int
或unsigned int
。使用原型,float
参数作为 a 传递float
(除非函数是可变参数,如printf
,在这种情况下,旧规则适用于可变参数)。
C Rationale文档在第6.7.5.3 节中讨论了这一点,可能比我的更好:
函数原型机制是 C 语言最有用的补充之一。当然,该功能在过去 25 年的许多源自 Algol 的语言中都有先例。标准中采用的特定形式在很大程度上基于 C++。
函数原型提供了强大的翻译时错误检测能力。在没有原型的传统 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 实现就不得不对所有函数遵循一个预先确定的“一刀切”的调用约定。