我刚刚遇到了一些人的C代码,我很困惑为什么它要编译。有两点我不明白。

与实际函数定义相比,函数原型没有参数。 函数定义中的形参没有类型。


#include <stdio.h>

int func();

int func(param)
{
    return param;
}

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

为什么会这样? 我已经在几个编译器中测试了它,它工作得很好。


当前回答

空参数列表表示“任何参数”,因此定义没有错。 缺失的类型被假定为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.

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型。

我认为任何通过这一点的构建都缺乏配置的警告/错误级别,尽管如此,允许实际代码没有任何意义。

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

函数声明器(包括原型)

标识符列表只声明函数参数的标识符。一个空 作为该函数定义的一部分的函数声明器中的列表指定 函数没有参数。类的一部分,函数声明器中的空列表 类的数量或类型的信息 提供参数。(如果两个函数类型都是“旧样式”,则不比较参数类型。)

函数声明器

使用带有空括号的函数声明符(而不是prototype-format参数) 类型声明器)是一个过时的特性。

函数定义

使用带有单独参数标识符和声明列表的函数定义 (而不是prototype-format参数类型和标识符声明器)是一个过时的特性。

旧的风格是指K&R风格吗

例子:

声明:int old_style();

定义:

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

如果函数声明没有形参,即为空,则它将接受不指定数量的参数。如果你想让它不带参数,那么将其更改为:

int func(void);