为什么没有参数的函数(与实际函数定义相比)编译?

时间:2012-12-19 10:48:39

标签: c parameters function-prototypes function-parameter

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

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

#include <stdio.h>

int func();

int func(param)
{
    return param;
}

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

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

11 个答案:

答案 0 :(得分:268)

所有其他答案都是正确的,但仅适用于completion

  

以下列方式声明函数:

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

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

     

function-name 是函数的名称。

     

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

再次为了完整起见。 From C11 specification 6:11:6(第179页)

  

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

答案 1 :(得分:158)

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

答案 2 :(得分:57)

int func();是一个过时的函数声明,从没有C标准的日子开始,即 K&amp; RC 的日子(1989年之前,第一个“ANSI C”标准的年份)已发表)。

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

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

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

答案 3 :(得分:53)

  • 空参数列表表示“任何参数”,因此定义没有错误。
  • 假设缺少的类型为int

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

答案 4 :(得分:29)

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

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

  

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

第6.11.6节函数声明符

  

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

第6.11.7节功能定义

  

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

旧样式的意思是 K&amp; R 样式

示例:

声明:int old_style();

定义:

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

答案 5 :(得分:15)

如果在函数返回类型和参数列表上没有给出类型,则

C假定为int。只有这个规则才能实现奇怪的事情。

函数定义如下所示。

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

如果它是你写的原型

int func(int param);

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

int func(int);

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

int func(param);

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

func();
编写int func()时,编译器会假定func()。但是不要将func()放在函数体中。这将是一个函数调用

答案 6 :(得分:11)

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

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

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

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

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

在linux下使用gcc进行编译的示例:

main.c

main2.c

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

像这样:

int myfunc(int param);

答案 7 :(得分:5)

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

使用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 :(得分:3)

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

int func(void);

答案 9 :(得分:0)

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

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 :(得分:0)

<块引用>

在旧式声明符中

<块引用>

标识符列表必须不存在,除非 声明符用于函数定义的头部 (第 A.10.1 段)。没有关于参数类型的信息 由声明提供。例如声明

int f(), *fpi(), (*pfi)();
<块引用>

声明一个返回整数的函数 f,一个返回一个整数指针的函数 fpi,以及一个返回一个整数的函数的指针 pfi。在这些中都没有>指定的参数类型;它们是旧式的。

<块引用>

在新式声明中

int strcpy(char *dest, const char *source), rand(void);

<块引用>

strcpy 是一个 函数返回 int,有两个参数,第一个是字符 指针,第二个是指向常量字符的指针

来源:- K&R 书籍

希望能消除你的疑惑..