提问



我只是碰到了某人的C代码,我很困惑它为什么要编译。有两点我不明白。


首先,与实际函数定义相比,函数原型没有参数。其次,函数定义中的参数没有类型。


#include 

int func();

int func(param)
{
    return param;
}

int main()
{
    int bla = func(10);    
    printf("%d", bla);
}


为什么这样做?我已经在几个编译器中对它进行了测试,它运行正常。

最佳参考


所有其他答案都是正确的,但只是为了完成[44]



??函数以下列方式声明:


  return-type function-name(parameter-list,...) { body... }

??
?? return-type 是函数返回的变量类型。这不能是数组类型或函数类型。 如果没有给出,那么int
??假设


??
?? function-name 是函数的名称。

??
?? parameter-list 是函数以逗号分隔的参数列表。 如果没有给出参数,那么该函数
??不带任何东西,应该用空集定义
??括号或关键字void。如果前面没有变量类型
??参数列表中的变量,则假设为
。数组和
??函数不会传递给函数,但会自动转换
??指针。如果列表以省略号(,...)结束,那么
??没有设定数量的参数。注意:标题stdarg.h可以
??用于在使用省略号时访问参数。



再次为了完整起见。从C11规范6:11:6(第179页)[45]



??使用带空括号的函数声明符(不是
??prototype-format参数类型声明符)是一个过时的
??特征
即可。


其它参考1


在C func()中意味着您可以传递任意数量的参数。如果你不想要参数,那么你必须声明为func(void)。您传递给函数的类型,如果未指定,则默认为int

其它参考2


int func();是一个过时的功能声明,从没有C标准的日子开始,即 K& RC 的日子(1989年之前,第一个ANSI C标准发布的那一年) 。


请记住,K& R C 中没有没有原型,关键字void尚未发明。您所能做的就是告诉编译器函数的返回类型。 K& R C中的空参数列表表示未指定但固定的参数数量。修复意味着您必须每次使用相同数量的args调用该函数(而不是像printf那样的可变参数函数,其中数字和类型可以每次通话都有所不同)。


许多编译器会诊断出这个结构;特别是gcc -Wstrict-prototypes会告诉你函数声明不是原型,这是现货,因为它看起来像原型(特别是如果你被C ++中毒了!),但是吨。这是一种旧式的K& R C返回类型声明。


经验法则:永远不要将空参数列表声明留??空,请int func(void)具体使用。
这将K& R返回类型声明转换为适当的C89原型。编译器很高兴,开发人员很高兴,静态跳棋很开心。那些被C ++误导的人可能会感到畏缩,因为他们在尝试锻炼外语时需要输入额外的字符:-)

其它参考3



  • 空参数列表表示任何参数,因此定义不是错误的。

  • 假设缺失的类型为int



我会认为任何传递此内容的构建都缺少配置的警告/错误级别,但是没有必要允许实际的代码。

其它参考4


它是 K& R 样式函数声明和定义。来自C99标准(ISO/IEC 9899:TC3)


第6.7.5.3节函数声明符(包括原型)



??标识符列表仅声明函数参数的标识符。一个空的
??函数声明符中的列表是该函数定义的一部分,用于指定
??功能没有参数。 函数声明符中不属于a的空列表
??该函数的定义指定没有关于数量或类型的信息
??提供参数。 (如果两种函数类型都是旧样式,则不比较参数类型。)




第6.11.6节函数声明符



??使用带有空括号的函数声明符(不是prototype-format参数
??类型声明者)是过时的功能。



第6.11.7节函数定义



??使用具有单独参数标识符和声明列表的函数定义
??(不是prototype-format参数类型和标识符声明符)是过时的功能。



哪种旧式意味着 K& R 风格


例:


声明:int old_style();


定义:


int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}

其它参考5


如果在函数返回类型和参数列表上没有给出类型,则 C假定int。只有这个规则才能实现奇怪的事情。


函数定义如下所示。


int func(int param) { /* body */}


如果它是你写的原型


int func(int param);


在原型中,您只能指定参数的类型。参数name不是必需的。所以


int func(int);


此外,如果您不指定参数类型,但名称int被假定为类型。


int func(param);


如果你走得更远,下面的工作也是如此。


func();


当您编写func()时,编译器会int func()。但是不要把func()放在函数体中。那将是一个函数调用

其它参考6


正如@Krishnabhadra所述,之前所有其他用户的回复都有正确的解释,我只想对某些要点进行更详细的分析。


在旧C中,如ANSI-C中的无类型形式参数,在8位MPU中取您的工作寄存器或指令深度能力(影子寄存器或指令累积周期)的尺寸,在16位MPU中是一个int16,因此将是一个int16,在这种情况下,64位架构可以选择编译选项,如:-m32。


虽然在高级别上看起来似乎更简单,
对于传递多个参数,程序员在控制维数数据类型步骤中的工作变得更加苛刻。


在其他情况下,对于某些微处理器体系结构,ANSI编译器定制,利用这些旧功能中的一些来优化代码的使用,迫使这些无类型正式参数的位置在工作寄存器内部或外部工作,今天你得到使用volatile和register几乎相同。


但应该指出最现代的编译器,
不要对两种类型的参数声明做任何区分。



在linux下用gcc编译的例子:












在任何情况下本地原型的声明都是没有用的,因为没有参数的调用没有参考这个原型会被忽略。
如果您使用带有无类型形式参数的系统,则对于外部调用,请继续生成声明性原型数据类型。


喜欢这个:


int myfunc(int param);

其它参考7


关于参数类型,这里已经有正确的答案,但是如果你想从编译器中听到它,你可以尝试添加一些标志(标志几乎总是一个好主意)。


使用gcc foo.c -Wextra编译程序我得到:


foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]


奇怪的是-Wextra并没有抓住这clang(由于某种原因它没有认识到-Wmissing-parameter-type,也许对于上面提到的历史事件而言,但-pedantic的确如此:


foo.c:5:10: warning: parameter 'param' was not declared, 
defaulting to type 'int' [-pedantic]
int func(param)
         ^
1 warning generated.


对于上面再次提到的原型问题int func()指的是任意参数,除非你明确地将其定义为int func(void),然后会按预期给出错误:


foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here


clang中:


foo.c:5:5: error: conflicting types for 'func'
int func(param)
    ^
foo.c:3:5: note: previous declaration is here
int func(void);
    ^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
    int bla = func(10);
              ~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.

其它参考8


如果函数声明没有参数,即空,那么它将采用未指定数量的参数。如果你想让它不带参数,那么将它改为:


int func(void);

其它参考9


这就是为什么我通常建议人们用以下代码编译代码:


cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition


这些标志强制执行以下几项操作:



  • -Wmissing-variable-declarations:如果不先获取原型,就不可能声明非静态函数。这使得头文件中的原型更有可能与实际定义匹配。或者,它强制您将static关键字添加到不需要公开显示的函数中。

  • -Wstrict-variable-declarations:原型必须正确列出参数。

  • -Wold-style-definition:函数定义本身也必须正确列出参数。



默认情况下,许多开源项目中也使用这些标志。例如,在Makefile中使用WARNS=6构建时,FreeBSD启用了这些标志。

其它参考10


先生,在C ++(和C ++ ONLY )中,您可以使用不同的参数定义多个具有相同名称的功能。例如:


int func();
int func(int test);
int func(char testing123);


应该编译。要选择要使用的函数,只需在编译时将该变量类型传递给括号。


例如:


int testing123=2;
func(testing123);


将调用func(int test)。





char test='a';
func(test);


将调用func(char)。


你不需要在函数头中使用变量名,虽然只要函数原型(y知道,顶部的行只有一个没有代码的函数)与下面的实际函数中的名称匹配,你就会成为A好的(例如,代替int func(int)你也可以拥有int func(int avariable).


对于没有类型的原型编译中的变量,它可能默认为type,可能是int(虽然我不确定它默认是由编译器改变的类型。)