我刚刚遇到了一些人的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构建时启用了这些标志。