为了真正符合标准,C 中的所有函数(除了 main)是否都必须有一个原型,即使它们只是在同一个翻译单元中定义后才使用?
6 回答
这取决于您所说的“真正符合标准”的含义。然而,简短的回答是“在使用之前确保所有函数在范围内都有一个原型是一个好主意”。
一个更合格的答案指出,如果函数接受可变参数(特别是printf()
函数族),那么原型必须在严格符合标准的范围内。C89(来自 ANSI)和 C90(来自 ISO;与 C89 相同,除了部分编号)也是如此。但是,除了 'varargs' 函数之外,int
不必声明返回 an 的函数,并且返回非 an 的函数int
确实需要一个显示返回类型但不需要参数列表的原型的声明。
但是请注意,如果函数采用在没有原型的情况下受“正常提升”约束的参数(例如,采用 achar
或short
- 两者都转换为int
的函数;更严重的是,可能采用afloat
而不是 a double
),则需要一个原型。标准对此很宽松,以允许旧的 C 代码在符合标准的编译器下编译;编写较旧的代码不是为了担心确保在使用之前声明函数 - 根据定义,较旧的代码不使用原型,因为它们在 C 中不可用,直到有一个标准。
C99 不允许 'implicit int' ...这意味着像 ' static a;
' (int
默认情况下)这样的奇怪情况以及隐式函数声明。在 ISO/IEC 9899:1999 的前言中提到了这些(以及大约 50 项其他重大变化),该标准将该标准与以前的版本进行了比较:
- 删除隐式
int
…- 删除隐式函数声明
在 ISO/IEC 9899:1990 中,§6.3.2.2函数调用声明:
如果函数调用中带括号的参数列表之前的表达式仅包含一个标识符,并且如果该标识符没有可见的声明,则该标识符被隐式声明,就好像在包含函数调用的最里面的块中,声明:
extern int identifier();
出现了。38
38也就是说,具有块范围的标识符声明为与类型函数具有外部链接,没有参数信息并返回
int
. 如果事实上它没有被定义为具有“函数返回int
”类型,那么行为是未定义的。
1999 年标准中缺少这一段。我还没有(还)跟踪static a;
在 C90 中允许和static int a;
在 C99 中不允许(要求)的措辞变化。
请注意,如果一个函数是静态的,它可以在使用之前定义,并且不需要在前面声明。如果在定义非静态函数之前没有声明 ( -Wmissing-prototypes
),那么 GCC 可能会被说服。
原型是指定函数参数类型的函数声明。
Pre-ANSI C(1978 年第一版 Kernighan & Ritchie 的“The C Programming Language”所描述的语言)没有原型;函数声明不可能描述参数的数量或类型。由调用者传递正确数量和类型的参数。
ANSI C 引入了“原型”,即指定参数类型的声明(从早期 C++ 借用的特性)。
从 C89/C90(ANSI 和 ISO 标准描述相同的语言)开始,调用没有可见声明的函数是合法的;提供了一个隐式声明。如果隐式声明与实际定义不兼容(例如,调用sqrt("foo")
,则行为未定义。此隐式声明和非原型声明都不能与可变参数函数兼容,因此对可变参数函数的任何调用(如printf
or scanf
)必须有一个可见的原型。
C99 删除了隐式声明。任何对没有可见声明的函数的调用都是违反约束的,需要编译器诊断。但是该声明仍然不需要是原型;它可以是不指定参数类型的旧式声明。
C11 在这方面没有做出重大改变。
因此,即使在 2011 年 ISO C 标准中,旧式函数声明和定义(自 1989 年以来已“过时”)仍然允许在符合代码中。
对于可追溯到 1989 年的所有 C 版本,就风格而言,几乎没有理由不对所有函数使用原型。保留旧式声明和定义只是为了避免破坏旧代码。
不,函数并不总是需要原型。唯一的要求是在使用之前“声明”一个函数。声明函数有两种方法:编写原型,或编写函数本身(称为“定义”)。定义始终是声明,但并非所有声明都是定义。
编写新函数时的一个很好的技巧是把它们倒过来写,main 在底部,这样当你改变对函数的参数或返回类型的想法时,你也不必修复原型。不断地修复原型,并在它们过时时处理所有编译器的警告变得非常乏味。
一旦你的函数一起顺利运行,将代码移动到一个命名良好的模块中,并将原型放在一个同名的 .h 文件中。它可以节省大量时间。5 年来我发现的最大的生产力辅助工具。
是的,每个函数都必须有一个原型,但该原型可能出现在单独的声明中或作为函数定义的一部分。用 C89 及更高版本编写的函数定义自然有原型,但如果你用经典的 K&R 风格编写东西,那么:
main (argc, argv)
int argc;
char **argv;
{
...
}
那么函数定义没有原型。如果您编写 ANSI C (C89) 样式,则:
main (int argc, char **argv) { ... }
那么函数定义就有了原型。
据我所知(在 ANSI C89/ISO C90 中),没有。我不确定 C99;但是,我也希望如此。
个人说明:我只写函数原型...
- 我需要(当 A() 调用 B()并且B() 调用 A()),或者
- 我正在导出函数;否则,感觉是多余的。