【发布时间】:2026-01-03 23:15:01
【问题描述】:
关于 ANSI C 函数声明,这对旧的 K&R 风格有何改进?我知道它们之间的区别,我只是想知道使用旧样式会出现什么问题,以及新样式是如何改进的。
【问题讨论】:
-
我看到有人投票结束这个。我同意这个问题可能含糊不清,但我相信有一个具体的答案(见我的)。
标签: c
关于 ANSI C 函数声明,这对旧的 K&R 风格有何改进?我知道它们之间的区别,我只是想知道使用旧样式会出现什么问题,以及新样式是如何改进的。
【问题讨论】:
标签: c
旧式函数声明,特别是不允许在编译时检查调用。
例如:
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 的语言。具体形式 标准中采用的很大程度上基于 C++。
函数原型提供了强大的翻译时错误 检测能力。在没有原型的传统 C 实践中,它 译者很难发现错误(错误 参数的数量或类型)在对另一个声明的函数的调用中 源文件。在运行时检测到此类错误 或通过使用辅助软件工具。
在函数原型范围之外的函数调用中,整数 参数应用了 integer Promotion 和 float 参数扩展为 double。在这样的电话中是不可能的 传递未转换的 char 或 float 参数。功能 原型让程序员明确控制函数 参数类型转换,因此经常不适当和 有时,参数的低效默认扩展规则可能是 被实施压制。
还有更多;去读吧。
【讨论】:
int func(); 中的 some_header.h。完整的 定义 确实提供了该信息,但是 (a) 它可能不在同一个源文件中,并且 (b) 编译器不需要使用它,即使它是。使用无效参数调用非原型函数具有未定义的行为;它不需要被诊断出来。
K&R 中的一个非定义函数声明如下所示
int foo();
并引入了一个接受未指定个参数的函数。这种声明风格的问题很明显:它既没有指定参数的数量,也没有指定它们的类型。编译器无法根据调用点的参数数量或其类型检查调用的正确性。在参数类型与预期参数类型不匹配的情况下,编译器无法执行参数类型转换或问题和错误消息。
函数声明,在K&R中用作函数定义的一部分,如下所示
int foo(a, b)
int a;
char b;
{ ...
它指定了参数的数量,但仍然没有指定它们的类型。此外,即使这个声明似乎暴露了参数的数量,它仍然像int foo();一样正式声明foo,这意味着将其称为foo(1, 2, 3, 4, 5)仍然不构成违反约束。
新风格,即使用原型声明更好,原因很明显:它公开了参数的数量和类型。它强制编译器检查调用的有效性(关于参数的数量和类型)。并且它允许编译器执行从参数类型到参数类型的隐式类型转换。
原型声明还提供了其他不太明显的好处。由于调用者和函数本身都知道函数参数的数量和类型,因此可以在调用时选择最有效的方法来传递参数(调用约定),而无需查看函数定义。如果没有这些信息,K&R 实现就不得不对所有函数遵循一个预先确定的“一刀切”的调用约定。
【讨论】: