【发布时间】:2010-12-07 19:24:57
【问题描述】:
我对 C 语言比较陌生。我遇到了一种我以前从未见过的函数语法形式,其中参数类型是在该参数列表之后定义的。谁能向我解释一下它与典型的 C 函数语法有何不同?
例子:
int main (argc, argv)
int argc;
char *argv[];
{
return(0);
}
【问题讨论】:
我对 C 语言比较陌生。我遇到了一种我以前从未见过的函数语法形式,其中参数类型是在该参数列表之后定义的。谁能向我解释一下它与典型的 C 函数语法有何不同?
例子:
int main (argc, argv)
int argc;
char *argv[];
{
return(0);
}
【问题讨论】:
这是参数列表的旧式语法,仍然受支持。在 K&R C 中,您还可以省略类型声明,它们将默认为 int。即
main(argc, argv)
char *argv[];
{
return 0;
}
将是相同的功能。
【讨论】:
int,因此必须显式声明。
还有一个有趣的地方是有原型的函数和没有原型的函数在调用约定上的区别。考虑一个旧样式定义:
void f(a)
float a; {
/* ... */
}
在这种情况下,调用约定是在传递给函数之前提升所有参数(例如,float 参数在传递之前首先提升为 double)。因此,如果f 接收到double 但参数的类型为float(这是完全有效的),编译器必须在执行函数体之前发出将双精度转换为浮点的代码。
如果你包含一个原型,编译器就不会再做这种自动提升,并且任何传递的数据都会被转换为原型参数的类型,就像通过赋值一样。所以以下是不合法的,并导致未定义的行为:
void f(float a);
void f(a)
float a; {
}
在这种情况下,函数的定义会将提交的参数从double(提升的形式)转换为float,因为定义是老式的。但是参数是作为浮点数提交的,因为函数有原型。比如clang给出了
main.c:3:9: 警告:K&R 函数参数的提升类型“double”与先前原型 [-Wknr-promoted-parameter] 中声明的参数类型“float”不兼容
解决矛盾的方法有以下两种:
// option 1
void f(double a);
void f(a)
float a; {
}
// option 2
// this declaration can be put in a header, but is redundant in this case,
// since the definition exposes a prototype already if both appear in a
// translation unit prior to the call.
void f(float a);
void f(float a) {
}
如果您可以选择,应该首选选项 2,因为它预先摆脱了旧样式定义。如果一个函数的这种相互矛盾的函数类型出现在同一个翻译单元中,编译器通常会告诉你(但不是必需的)。如果这样的矛盾出现在多个翻译单元上,错误可能会被忽视,并可能导致难以预测的错误。最好避免使用这些旧式定义。
【讨论】:
这是所谓的 K&R 风格 或 old-style 声明。
请注意,此声明与现代声明显着不同。 K&R 声明没有为函数引入 prototype,这意味着它不会将参数的类型暴露给外部代码。
【讨论】:
没有区别,只是那是 C 中函数声明的旧语法——它是在 ANSI 之前使用的。 切勿编写此类代码,除非您打算将其提供给 80 年代的朋友。此外,永远不要依赖隐式类型假设(正如另一个答案似乎暗示的那样)
【讨论】:
虽然函数定义的旧语法仍然有效(如果您询问编译器,则会出现警告),但使用它们并不能提供函数原型。
如果没有函数原型,编译器将不会检查函数是否被正确调用。
#include <stdio.h>
int foo(c)
int c;
{ return printf("%d\n", c); }
int bar(x)
double x;
{ return printf("%f\n", x); }
int main(void)
{
foo(42); /* ok */
bar(42); /* oops ... 42 here is an `int`, but `bar()` "expects" a `double` */
return 0;
}
程序运行时,我机器上的输出是
$ gcc proto.c
$ gcc -Wstrict-prototypes proto.c
proto.c:4: warning: function declaration isn’t a prototype
proto.c:10: warning: function declaration isn’t a prototype
$ ./a.out
42
0.000000
【讨论】:
它只是相同但旧时尚。您可能发现它是一些旧的遗留代码。
【讨论】:
旧与否,我会争论什么是旧的,什么不是......就像金字塔很古老,但今天所谓的科学家们都不知道它们是如何制造的。回顾过去,旧程序今天仍然可以正常工作,没有内存泄漏,但这些“新”程序往往会失败。我在这里看到了一个趋势。
可能他们将函数视为具有可执行主体的结构。这里需要 ASM 的知识来解开这个谜。
编辑,找到一个宏,表明您根本不需要提供参数名称。
#ifndef OF /* function prototypes */
# ifdef STDC
# define OF(args) args
# else
# define OF(args) ()
# endif
#endif
#ifndef Z_ARG /* function prototypes for stdarg */
# if defined(STDC) || defined(Z_HAVE_STDARG_H)
# define Z_ARG(args) args
# else
# define Z_ARG(args) ()
# endif
#endif
这是一个用法示例,库是zlib-1.2.11。
ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
所以我的第二个猜测是函数重载,否则这些参数没有用。一个具体的函数,现在有无数同名的函数。
【讨论】: