我刚刚遇到了一些人的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);
}
为什么会这样? 我已经在几个编译器中测试了它,它工作得很好。
当前回答
这就是为什么我通常建议人们使用以下方法编译代码:
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构建时启用了这些标志。
其他回答
这就是为什么我通常建议人们使用以下方法编译代码:
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构建时启用了这些标志。
int func ();是一个过时的函数声明,从没有C标准的日子,即K&R C的日子(1989年之前,第一个“ANSI C”标准发布)。
记住,在《K&R C》中还没有原型,关键字void也还没有被发明出来。你所能做的就是告诉编译器函数的返回类型。K&R C中的空参数列表表示“未指定但固定”的参数数量。Fixed意味着每次调用函数时必须使用相同数量的参数(而不是像printf这样的可变参数函数,每次调用时参数的数量和类型都可以变化)。
许多编译器会诊断这个构造;特别是gcc - wstrict - prototyping会告诉你“函数声明不是原型”,这是正确的,因为它看起来像一个原型(特别是如果你被c++毒害了!),但不是。这是一个老式的K&R C返回类型声明。
经验法则:永远不要让空参数列表声明为空,具体使用int func(void)。 这将K&R返回类型声明转换为适当的C89原型。编译器很高兴,开发人员很高兴,静态检查器也很高兴。不过,那些被c++的^W^Wfond误导的人可能会畏缩,因为当他们尝试练习外语技能时,他们需要输入额外的字符:-)
空参数列表表示“任何参数”,因此定义没有错。 缺失的类型被假定为int型。
我认为任何通过这一点的构建都缺乏配置的警告/错误级别,尽管如此,允许实际代码没有任何意义。
如果函数返回类型和形参列表上没有给出类型,则C语言假定为int。只有遵循这条规则,才有可能发生奇怪的事情。
函数定义是这样的。
int func(int param) { /* body */}
如果它是一个原型,你写
int func(int param);
在原型中,你只能指定参数的类型。参数名称为非必选项。所以
int func(int);
同样,如果你没有指定参数类型,但名称int被假设为类型。
int func(param);
如果你走得更远,跟着也行。
func();
当你编写func()时,编译器假设int func()。但是不要把func()放在函数体中。这是一个函数调用
关于参数类型,这里已经有了正确的答案,但如果你想从编译器那里听到它,你可以尝试添加一些标志(标志几乎总是一个好主意)。
使用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.