我对C语言还比较陌生。我遇到了一种从未见过的函数语法形式,其中参数类型是在参数列表之后定义的。有人能解释一下它与典型的C函数语法有何不同吗?
示例:
int main (argc, argv)
int argc;
char *argv[];
{
return(0);
}
这是旧式参数列表的语法,目前仍然支持。在K&R C中,您也可以省略类型声明,它们会默认为int。也就是说:
main(argc, argv)
char *argv[];
{
return 0;
}
将是相同的函数。
void f(a)
float a; {
/* ... */
}
float
参数首先被提升为double
,然后再传递)。因此,如果f
接收到一个double
,但参数的类型是float
(这是完全有效的),编译器必须发出将double转换为float的代码,然后执行函数体。void f(float a);
void f(a)
float a; {
}
// 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) {
}
这是所谓的K&R风格或旧式声明。
请注意,这种声明与现代声明显著不同。K&R声明不会为函数引入原型,也就是说它不会将参数类型暴露给外部代码。
其实它们没有区别,只是这是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
这只是同样的东西,但已经过时了。你可能会发现它是一些旧的、遗留的代码。
无论新旧,我认为什么是老的,什么不是老的都可以争论。比如金字塔是古代的,但今天的所谓科学家们都不知道它们是如何建造的。回顾过去,旧程序仍然能够在今天正常运行而不会出现内存泄漏,但这些“新”程序往往更容易失败。我看到了一个趋势。
也许他们将函数视为具有可执行体的结构体。需要了解汇编语言才能解决这个谜团。
编辑,发现一个宏,表明您根本不需要提供参数名称。
#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));
所以我的第二个猜测是函数重载,否则这些参数就没有用处。一个具体的函数,现在有无限数量的同名函数。