C函数语法,参数列表后声明的参数类型

C函数语法,参数列表后声明的参数类型,c,syntax,function,C,Syntax,Function,我对C比较陌生。我遇到了一种以前从未见过的函数语法形式,其中参数类型是在参数列表之后定义的。有人能给我解释一下它与典型的C函数语法有什么不同吗 例如: int main (argc, argv) int argc; char *argv[]; { return(0); } 这是参数列表的旧式语法,现在仍然受支持。在K&R C中,您也可以省略类型声明,它们将默认为int main(argc, argv) 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;
}

将是相同的函数。

没有区别,只是这是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;
}
这是所谓的调用方K&R样式或旧式声明


请注意,此声明与现代声明有很大不同。K&R声明没有为函数引入原型,这意味着它不会向外部代码公开参数的类型。

有趣的是,有原型的函数和没有原型的函数的调用约定不同。考虑一个旧的风格定义:

void f(a)
 float a; {
 /* ... */
}
在这种情况下,调用约定是所有参数在传递给函数之前都会被提升(例如,
float
参数在传递之前首先被提升为
double
)。因此,如果
f
接收到一个
double
,但参数的类型为
float
(这是完全有效的),编译器必须在执行函数体之前发出将double转换为float的代码

如果包含原型,编译器将不再执行此类自动升级,传递的任何数据将转换为原型的参数类型,就像通过赋值一样。因此,以下行为不合法,并导致未定义的行为:

void f(float a);
void f(a)
  float a; {

}
在这种情况下,函数的定义会将提交的参数从
double
(升级形式)转换为
float
,因为该定义是旧式的。但是参数是以浮点形式提交的,因为函数有一个原型。例如,叮当声

main.c:3:9:警告:K&R函数参数的升级类型“double”与先前原型中声明的参数类型“float”不兼容[-Wknr升级参数]

解决这些矛盾的方法有以下两种:

// 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应该是首选的,因为它预先去掉了旧的样式定义。如果某个函数的此类矛盾函数类型出现在同一翻译单元中,编译器通常会告诉您(但不是必需的)。如果这种矛盾出现在多个翻译单元上,错误可能会被忽略,并可能导致难以预测的错误。最好避免这些旧式的定义。

旧与否,我会争论什么是旧,什么不是。。就像金字塔一样,它们也是古老的,但今天所谓的科学家都不知道它们是如何建造的。回顾过去,旧程序今天仍然可以正常工作,没有内存泄漏,但这些“新”程序往往失败的次数更多。我看到了一种趋势

他们可能将函数视为具有可执行体的结构。这里需要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
下面是一个使用示例,库是


所以我的第二个猜测是函数重载,否则这些参数就没用了。一个具体的函数,现在有无数个同名函数。

Well pre C99;它是在美国国家标准化协会(ANSI)之前使用的,该协会于1989年标准化,但该过程始于1983年。。。C99仍然正式支持K&R风格的声明,只做了一个小小的改动——不再有“隐式int”,所有参数都必须显式声明。在批评声中,我必须说我同意你的看法:不要这样做。C89/90和C99仍然正式支持K&R风格的声明。但是,在C99中,所有参数都必须显式声明(不再有“隐式int”规则)。我讨厌迟到(比如4年后?),但您的意思是说在C99中,它们需要在参数列表或函数中显式声明吗?例如,默认的int规则-由于它们不再默认为int,因此如果参数列表中没有提供类型,则在使用之前需要在函数中声明它们的类型?@ChrisCirefice在4年后回答您:在C99中,所有参数都必须在参数列表中声明。函数中声明的变量从未默认为
int
,因此必须显式声明。
#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
ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));