繁体   English   中英

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

[英]Why does a function with no parameters (compared to the actual function definition) compile?

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

  1. 与实际的函数定义相比,函数原型没有参数。

  2. 函数定义中的参数没有类型。


#include <stdio.h>

int func();

int func(param)
{
    return param;
}

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

为什么这样做? 我已经在几个编译器中对其进行了测试,并且运行良好。

所有其他答案都是正确的,但只是为了完成

函数的声明方式如下:

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

return-type是函数返回的变量类型。 这不能是数组类型或函数类型。 如果未给出,则假定为 int

function-name函数的名称

参数列表是函数采用的参数列表,以逗号分隔。 如果没有给出参数,则该函数不接受任何参数,应使用空括号或关键字 void 进行定义。 如果参数列表中的变量前面没有变量类型,则假定为 int 数组和函数不会传递给函数,而是自动转换为指针。 如果列表以省略号 (,...) 结尾,则没有设定数量的参数。 注意:使用省略号时,标头 stdarg.h 可用于访问参数。

再次为完整起见。 来自 C11 规范 6:11:6 (第 179 页)

使用带空括号的函数声明符(不是原型格式参数类型声明符)是一个过时的特性

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

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

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

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

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

  • 空参数列表意味着“任何参数”,所以定义没有错。
  • 缺少的类型假定为int

我会认为任何通过这个的构建都缺乏配置的警告/错误级别,但允许实际代码没有意义。

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

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

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

第 6.11.6 节函数声明符

使用带空括号的函数声明符(不是原型格式的参数类型声明符)是一个过时的特性。

第 6.11.7 节函数定义

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

旧风格意味着K&R风格

例子:

声明: int old_style();

定义:

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

如果在函数返回类型和参数列表上没有给出类型,C 假定为int 只有遵循奇怪的事情的这条规则才是可能的。

函数定义如下所示。

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

如果它是你写的原型

int func(int param);

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

int func(int);

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

int func(param);

如果你走得更远,跟随也有效。

func();

当您编写func()时,编译器假定为int func() func() 但是不要将func()放在函数体内。 那将是一个函数调用

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

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

虽然在高层看起来实现起来更简单,但对于传递多个参数,程序员在控制维度数据类型步骤的工作变得更加苛刻。

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

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

linux下gcc编译示例:

主文件

main2.c

main3.c
无论如何,在本地声明原型是没有用的,因为没有调用没有参数引用这个原型将被遗漏。 如果使用带有“无类型形式参数”的系统,对于外部调用,继续生成声明性原型数据类型。

像这样:

int myfunc(int param);

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

使用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.

如果函数声明没有参数,即为空,则它采用未指定数量的参数。 如果您想让它不带任何参数,请将其更改为:

int func(void);

这就是为什么我通常建议人们编译他们的代码:

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

这些标志强制执行以下几件事:

  • -Wmissing-variable-declarations:如果不先获得原型,就不可能声明非静态函数。 这使得头文件中的原型更有可能与实际定义匹配。 或者,它强制您将 static 关键字添加到不需要公开可见的函数中。
  • -Wstrict-variable-declarations:原型必须正确列出参数。
  • -Wold-style-definition:函数定义本身也必须正确列出参数。

许多开源项目也默认使用这些标志。 例如,FreeBSD 在 Makefile 中使用 WARNS=6 构建时启用了这些标志。

在旧式声明符中

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

int f(), *fpi(), (*pfi)();

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

在新式声明中

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

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

资料来源:- K&R 书

希望能解开你的疑惑。。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM