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

  显示原文与译文双语对照的内容

我刚刚遇到了一些人的代码,我感到困惑,为什么它正在编译。 有两点我不理解。

首先,函数Prototype没有与实际函数定义相比的参数。 第二,函数定义中的参数没有类型。


#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 是函数的名称。

parameter-list 是函数用逗号分隔的参数的列表。 如果未指定参数,则为磅 ,函数不接受任何参数,应该用空括号或者关键字void定义。 如果变量列表中的变量前面没有变量类型,则假定int为 。 数组和函数没有传递给函数,而是自动转换为指针。 如果列表以省略号( 。。) 终止,则没有设置参数的数目。 注意:当使用省略号时,标题 stdarg.h 可以用来访问参数。

再一次为了完整。 从c11规范 6: 11: 6 (页面: 179 )

使用函数declarators空括号 ( 不是prototype-format参数类型 declarators ) 是一个荒废的特性。

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

  • 空参数列表意味着"任何参数",所以定义不是错误的。
  • 缺少的类型假定为 int

我将考虑任何通过它的构建都缺少配置的警告/错误级别,但这在实际代码中没有意义。

int func(); 是一个荒废的函数声明的日子没有c标准, 换句话说,的日子 k& R C ( 在 1989之前,第一个是"ansi C"标准已经发布) 。

记住,没有原型在 k& R C 和关键字 void 还没有发明。 你所能做的就是告诉编译器关于函数的返回类型 。 k& R 中的空参数列表表示参数数目。 固定意味着你必须调用的函数相同 args ( 而不是可变像 printf 函数,在每次调用的数量和类型可以不同) 每次的数量。

许多编译器将诊断这种构造;特别是 gcc -Wstrict-prototypes 会告诉你"函数声明不是 Prototype",位置,因为它像( 尤其是如果你被 C++ 中毒了) 原型,但不是。 这是旧式的k& R 返回类型声明。

的规则: 从不保留空参数列表声明为空,使用 int func(void) 作为特定参数。 这将 k& R 返回类型声明转换为一个适当的C89 Prototype 。 编译器很高兴,开发者很高兴,静态跳棋也很高兴。 那些误导by^W^Wfond的C++ 可能会畏缩,因为他们需要在尝试练习外语技能时键入额外的字符:- )

这是 k& R 样式函数声明和定义。 从C99标准( iso/iec 9899: tc3 )

节 6.7.5.3 函数 Declarators ( 包括 Prototype )

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

第 6.11.6节函数 declarators

使用函数declarators和空括号( 不是prototype-format参数类型 declarators ) 是一个的过时特性。

第 6.11.7节函数定义

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

旧样式意味着 k& R 风格

例如:

声明:int old_style()

定义:


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

如果函数返回类型和参数列表中没有给定类型,则为 int assumes 。 只有这样的规则才有可能发生奇怪的事情。

函数定义如下所示。


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

如果它是你写的Prototype

 
int func(int param);

 

在Prototype中,你只能指定参数的类型。 参数'名称is不是强制的。 所以

 
int func(int);

 

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

 
int func(param)

 

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

 
func()

 

编译器假定 int func()func() 当你写。 但不要将 func() 放在函数体内。 这将是一个函数调用

就像前面提到的@Krishnabhadra, 所有来自其他用户的响应,有一个正确的解释,我想对一些点进行更详细的分析。

在Old-C中,如ANSI-C中的"非类型化形参",接受工作寄存器或者指令深度能力( 阴影寄存器或者指令累积循环)的dimencion,在 8位微处理器中,将是 int16,在 16位微处理器中,可以选择编译选项,如: -m32.

虽然看起来简单实现了高水平的传递多个参数,程序员的工作在控制dimencion数据类型一步,变得更加苛刻。

在其他情况下,对于某些微处理器架构,ANSI编译器定制了一些旧特性,利用这些旧特性来优化代码的使用,迫使这些"非类型化形参"在工作寄存器内或者在工作寄存器中使用"volatile"和"注册"。

,但应该注意的是最现代的编译器,而不区分两种类型的参数声明。

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

main.c

main2.c

main3.c
在任何情况下,本地Prototype的声明都是没有用的,因为没有没有参数引用的调用将是错误的。 如果你使用"非类型化形参"系统,对于外部调用,请继续生成声明性Prototype数据类型。

就像这个:


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() 指任意参数,除非你exclicitly它定义为 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: 在不获得Prototype的情况下声明non-static函数是不可能的。 这使得头文件中的Prototype更符合实际的定义。 另外,它强制你将静态关键字添加到不需要公开可见的函数。
  • -Wstrict-variable-declarations: Prototype必须正确列出参数。
  • -Wold-style-definition: 函数定义本身也必须正确列出参数。

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

...