简体   繁体   English

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

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

I've just come across someone's C code that I'm confused as to why it is compiling.我刚刚遇到了某人的 C 代码,我对它为什么要编译感到困惑。 There are two points I don't understand.有两点不明白。

  1. The function prototype has no parameters compared to the actual function definition.与实际的函数定义相比,函数原型没有参数。

  2. The parameter in the function definition does not have a type.函数定义中的参数没有类型。


#include <stdio.h>

int func();

int func(param)
{
    return param;
}

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

Why does this work?为什么这样做? I have tested it in a couple of compilers, and it works fine.我已经在几个编译器中对其进行了测试,并且运行良好。

All the other answers are correct, but just for completion所有其他答案都是正确的,但只是为了完成

A function is declared in the following manner:函数的声明方式如下:

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

return-type is the variable type that the function returns. return-type是函数返回的变量类型。 This can not be an array type or a function type.这不能是数组类型或函数类型。 If not given, then int is assumed .如果未给出,则假定为 int

function-name is the name of the function. function-name函数的名称

parameter-list is the list of parameters that the function takes separated by commas.参数列表是函数采用的参数列表,以逗号分隔。 If no parameters are given, then the function does not take any and should be defined with an empty set of parenthesis or with the keyword void.如果没有给出参数,则该函数不接受任何参数,应使用空括号或关键字 void 进行定义。 If no variable type is in front of a variable in the paramater list, then int is assumed .如果参数列表中的变量前面没有变量类型,则假定为 int Arrays and functions are not passed to functions, but are automatically converted to pointers.数组和函数不会传递给函数,而是自动转换为指针。 If the list is terminated with an ellipsis (,...), then there is no set number of parameters.如果列表以省略号 (,...) 结尾,则没有设定数量的参数。 Note: the header stdarg.h can be used to access arguments when using an ellipsis.注意:使用省略号时,标头 stdarg.h 可用于访问参数。

And again for the sake of completeness.再次为完整起见。 From C11 specification 6:11:6 (page: 179)来自 C11 规范 6:11:6 (第 179 页)

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature .使用带空括号的函数声明符(不是原型格式参数类型声明符)是一个过时的特性

In C func() means that you can pass any number of arguments.在 C 中func()意味着您可以传递任意数量的参数。 If you want no arguments then you have to declare as func(void) .如果你不想要参数,那么你必须声明为func(void) The type you're passing to your function, if not specified defaults to int .您传递给函数的类型(如果未指定)默认为int

int func(); is an obsolescent function declaration from the days when there was no C standard, ie the days of K&R C (before 1989, the year the first "ANSI C" standard was published).是从没有 C 标准的日子开始的过时函数声明,即K&R C的日子(在 1989 年之前,即第一个“ANSI C”标准发布的那一年)。

Remember that there were no prototypes in K&R C and the keyword void was not yet invented.请记住, K&R C没有原型,关键字void尚未发明。 All you could do was to tell the compiler about the return type of a function.你所能做的就是告诉编译器一个函数的返回类型 The empty parameter list in K&R C means "an unspecified but fixed" number of arguments. K&R C 中的空参数列表意味着“未指定但固定”数量的参数。 Fixed means that you must call the function with the same number of args each time (as opposed to a variadic function like printf , where the number and type can vary for each call). Fixed 意味着您每次必须使用相同数量的 args 调用函数(而不是像printf这样的可变参数函数,每次调用的数量和类型都可能不同)。

Many compilers will diagnose this construct;许多编译器会诊断这个结构; in particular gcc -Wstrict-prototypes will tell you "function declaration isn't a prototype", which is spot on, because it looks like a prototype (especially if you are poisoned by C++!), but isn't.特别是gcc -Wstrict-prototypes会告诉你“函数声明不是原型”,这是正确的,因为它看起来像一个原型(特别是如果你被 C++ 毒害了!),但不是。 It's an old style K&R C return type declaration.这是一个旧式的 K&R C 返回类型声明。

Rule of thumb: Never leave an empty parameter list declaration empty, use int func(void) to be specific.经验法则:永远不要将空参数列表声明留​​空,使用int func(void)来具体说明。 This turns the K&R return type declaration into a proper C89 prototype.这将 K&R 返回类型声明转换为适当的 C89 原型。 Compilers are happy, developers are happy, static checkers are happy.编译器很高兴,开发人员很高兴,静态检查员很高兴。 Those mislead by^W^Wfond of C++ may cringe, though, because they need to type extra characters when they try to exercise their foreign language skills :-)那些被 ^W^Wfond of C++ 误导的人可能会畏缩,因为他们在尝试练习外语技能时需要输入额外的字符:-)

  • The empty parameter list means "any arguments", so the definition isn't wrong.空参数列表意味着“任何参数”,所以定义没有错。
  • The missing type is assumed to be int .缺少的类型假定为int

I would consider any build that passes this to be lacking in configured warning/error level though, there's no point in being this allowing for actual code.我会认为任何通过这个的构建都缺乏配置的警告/错误级别,但允许实际代码没有意义。

It's K&R style function declaration and definition.它是K&R风格的函数声明和定义。 From C99 Standard (ISO/IEC 9899:TC3)来自 C99 标准 (ISO/IEC 9899:TC3)

Section 6.7.5.3 Function Declarators (including prototypes)第 6.7.5.3 节函数声明符(包括原型)

An identifier list declares only the identifiers of the parameters of the function.标识符列表仅声明函数参数的标识符。 An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters.作为该函数定义的一部分的函数声明符中的空列表指定该函数没有参数。 The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.不属于该函数定义的函数声明符中的空列表指定不提供有关参数数量或类型的信息。 (If both function types are "old style", parameter types are not compared.) (如果两种函数类型都是“旧式”,则不会比较参数类型。)

Section 6.11.6 Function declarators第 6.11.6 节函数声明符

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.使用带空括号的函数声明符(不是原型格式的参数类型声明符)是一个过时的特性。

Section 6.11.7 Function definitions第 6.11.7 节函数定义

The use of function definitions with separate parameter identifier and declaration lists (not prototype-format parameter type and identifier declarators) is an obsolescent feature.使用具有单独参数标识符和声明列表(不是原型格式参数类型和标识符声明符)的函数定义是一个过时的功能。

Which the old style means K&R style旧风格意味着K&R风格

Example:例子:

Declaration: int old_style();声明: int old_style();

Definition:定义:

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

C assumes int if no type is given on function return type and parameter list .如果在函数返回类型和参数列表上没有给出类型,C 假定为int Only for this rule following weird things are possible.只有遵循奇怪的事情的这条规则才是可能的。

A function definition looks like this.函数定义如下所示。

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

If its a prototype you write如果它是你写的原型

int func(int param);

In prototype you can only specify the type of parameters.在原型中,您只能指定参数的类型。 Parameters' name is not mandatory.参数的名称不是强制性的。 So所以

int func(int);

Also if you dont specify parameter type but name int is assumed as type.此外,如果您不指定参数类型但名称int被假定为类型。

int func(param);

If you go farther, following works too.如果你走得更远,跟随也有效。

func();

Compiler assumes int func() when you write func() .当您编写func()时,编译器假定为int func() func() But dont put func() inside a function body.但是不要将func()放在函数体内。 That'll be a function call那将是一个函数调用

As stated @Krishnabhadra, all previous responses from other users, have a correct interpretation, and I just want to make a more detailed analysis of some points.正如@Krishnabhadra 所说,之前其他用户的所有回复都有正确的解释,我只是想对一些要点进行更详细的分析。

In the Old-C as in ANSI-C the " untyped formal parameter ", take the dimencion of your work register or instruction depth capability (shadow registers or instruction cumulative cycle), in an 8bit MPU, will be an int16, in a 16bit MPU and so will be an int16 an so on, in the case 64bit architectures may choose to compile options like: -m32.在 Old-C 和 ANSI-C 中的“无类型形式参数”,取你的工作寄存器或指令深度能力(影子寄存器或指令累积周期)的维度,在 8 位 MPU 中,将是一个 int16,在一个 16 位MPU 等将是 int16 等,在 64 位架构可能选择编译选项的情况下,如:-m32。

Although it seems simpler implementation at high level, For pass multiple parameters, the work of the programmer in the control dimencion data type step, becomes more demanding.虽然在高层看起来实现起来更简单,但对于传递多个参数,程序员在控制维度数据类型步骤的工作变得更加苛刻。

In other cases, for some microprocessors architectures, the ANSI compilers customized, leveraged some of this old features to optimize the use of the code, forcing the location of these "untyped formal parameters" to work within or outside the work register, today you get almost the same with the use of "volatile" and "register".在其他情况下,对于某些微处理器架构,ANSI 编译器定制,利用一些旧特性来优化代码的使用,迫使这些“无类型形式参数”的位置在工作寄存器内部或外部工作,今天你得到与使用“volatile”和“register”几乎相同。

But it should be noted that the most modern compilers, not make any distinction between the two types of parameters declaration.但应该注意的是,最现代的编译器,不会对这两种类型的参数声明做任何区分。

Examples of a compilation with gcc under linux: linux下gcc编译示例:

主文件

main2.c

main3.c
In any case the statement of the prototype locally is of no use, because there is no call without parameters reference to this prototype will be remiss.无论如何,在本地声明原型是没有用的,因为没有调用没有参数引用这个原型将被遗漏。 If you use the system with "untyped formal parameter", for an external call, proceed to generate a declarative prototype data type.如果使用带有“无类型形式参数”的系统,对于外部调用,继续生成声明性原型数据类型。

Like this:像这样:

int myfunc(int param);

Regarding parameter type, there are already correct answers here but if you want to hear it from the compiler you can try adding some flags (flags are almost always a good idea anyways).关于参数类型,这里已经有正确的答案,但是如果您想从编译器那里听到它,您可以尝试添加一些标志(无论如何,标志几乎总是一个好主意)。

compiling your program using gcc foo.c -Wextra I get:使用gcc foo.c -Wextra编译你的程序我得到:

foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]

strangely -Wextra doesn't catch this for clang (it doesn't recognize -Wmissing-parameter-type for some reason, maybe for historical ones mentioned above) but -pedantic does:奇怪的是-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.

And for prototype issue as said again above int func() refers to arbitrary parameters unless you exclicitly define it as int func(void) which would then give you the errors as expected:对于上面再次提到的原型问题, 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

or in clang as:或在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.

If the function declaration has no parameters ie empty then it is taking unspecified number of arguments.如果函数声明没有参数,即为空,则它采用未指定数量的参数。 If you want to make it take no arguments then change it to:如果您想让它不带任何参数,请将其更改为:

int func(void);

This is why I typically advise people to compile their code with:这就是为什么我通常建议人们编译他们的代码:

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

These flags enforce a couple of things:这些标志强制执行以下几件事:

  • -Wmissing-variable-declarations: It is impossible to declare a non-static function without getting a prototype first. -Wmissing-variable-declarations:如果不先获得原型,就不可能声明非静态函数。 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.或者,它强制您将 static 关键字添加到不需要公开可见的函数中。
  • -Wstrict-variable-declarations: The prototype must properly list the arguments. -Wstrict-variable-declarations:原型必须正确列出参数。
  • -Wold-style-definition: The function definition itself must also properly list the arguments. -Wold-style-definition:函数定义本身也必须正确列出参数。

These flags are also used by default in a lot of Open Source projects.许多开源项目也默认使用这些标志。 For example, FreeBSD has these flags enabled when building with WARNS=6 in your Makefile.例如,FreeBSD 在 Makefile 中使用 WARNS=6 构建时启用了这些标志。

In the old-style declarator ,在旧式声明符中

the identifier list must be absent unless the declarator is used in the head of a function definition (Par.A.10.1).除非在函数定义(Par.A.10.1)的头部使用了声明符,否则标识符列表必须不存在。 No information about the types of the parameters is supplied by the declaration.声明没有提供关于参数类型的信息。 For example, the declaration例如,声明

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

declares a function f returning an integer, a function fpi returning a pointer to an integer, >and a pointer pfi to a function returning an integer.声明了一个返回整数的函数 f,一个返回一个整数指针的函数 fpi,以及一个返回一个整数的函数的指针 pfi。 In none of these are the parameter types >specified;在这些中都没有>指定的参数类型; they are old-style.他们是老式的。

In the new-style declaration在新式声明中

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

strcpy is a function returning int, with two arguments, the first a character pointer, and the second a pointer to constant characters strcpy 是一个返回 int 的函数,有两个参数,第一个是字符指针,第二个是指向常量字符的指针

SOURCE:- K&R book资料来源:- K&R 书

I hope it cleared your doubt..希望能解开你的疑惑。。

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

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