我刚刚遇到了一些人的C代码,我很困惑为什么它要编译。有两点我不明白。
与实际函数定义相比,函数原型没有参数。 函数定义中的形参没有类型。
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
为什么会这样? 我已经在几个编译器中测试了它,它工作得很好。
我刚刚遇到了一些人的C代码,我很困惑为什么它要编译。有两点我不明白。
与实际函数定义相比,函数原型没有参数。 函数定义中的形参没有类型。
#include <stdio.h>
int func();
int func(param)
{
return param;
}
int main()
{
int bla = func(10);
printf("%d", bla);
}
为什么会这样? 我已经在几个编译器中测试了它,它工作得很好。
当前回答
空参数列表表示“任何参数”,因此定义没有错。 缺失的类型被假定为int型。
我认为任何通过这一点的构建都缺乏配置的警告/错误级别,尽管如此,允许实际代码没有任何意义。
其他回答
在C语言中,func()意味着你可以传递任意数量的参数。如果你不想要参数,那么你必须声明为func(void)。传递给函数的类型,如果没有指定,默认为int。
关于参数类型,这里已经有了正确的答案,但如果你想从编译器那里听到它,你可以尝试添加一些标志(标志几乎总是一个好主意)。
使用gcc foo.c编译你的程序
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
或在铿锵声中为:
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.
如果函数返回类型和形参列表上没有给出类型,则C语言假定为int。只有遵循这条规则,才有可能发生奇怪的事情。
函数定义是这样的。
int func(int param) { /* body */}
如果它是一个原型,你写
int func(int param);
在原型中,你只能指定参数的类型。参数名称为非必选项。所以
int func(int);
同样,如果你没有指定参数类型,但名称int被假设为类型。
int func(param);
如果你走得更远,跟着也行。
func();
当你编写func()时,编译器假设int func()。但是不要把func()放在函数体中。这是一个函数调用
正如@Krishnabhadra所说,其他用户之前的所有回答都有正确的解释,我只是想对一些观点做更详细的分析。
在Old-C中,就像在ANSI-C中一样,“无类型的形式参数”,取你的工作寄存器的尺寸或指令深度能力(阴影寄存器或指令累积周期),在一个8位MPU中,将是一个int16,在一个16位MPU中,将是一个int16,等等,在这种情况下,64位架构可以选择编译选项,如:-m32。
虽然它在高层的实现看起来更简单, 对于传递多个参数,程序员在控制维度数据类型步骤中的工作,变得更加苛刻。
在其他情况下,对于一些微处理器架构,ANSI编译器自定义,利用一些旧的特性来优化代码的使用,迫使这些“无类型的形式参数”的位置在工作寄存器内部或外部工作,今天你几乎可以使用“volatile”和“register”。
但值得注意的是,最现代的编译器, 不区分这两种类型的参数声明。
在linux下使用gcc编译的例子:
在任何情况下,原型的语句在本地是没有用的,因为没有参数的调用对这个原型的引用将是疏忽。 如果您使用带有“无类型形式参数”的系统进行外部调用,则继续生成声明性原型数据类型。
是这样的:
int myfunc(int param);
这就是为什么我通常建议人们使用以下方法编译代码:
cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition
这些标志执行了一些事情:
-Wmissing-variable-declarations: It is impossible to declare a non-static function without getting a prototype first. This makes it more likely that a prototype in a header file matches with the actual definition. Alternatively, it enforces that you add the static keyword to functions that don't need to be visible publicly. -Wstrict-variable-declarations: The prototype must properly list the arguments. -Wold-style-definition: The function definition itself must also properly list the arguments.
在许多开源项目中也默认使用这些标志。例如,FreeBSD在Makefile中使用warnings =6构建时启用了这些标志。