C89、C90 或 C99 中的所有函数都需要原型吗?
为了真正符合标准,C 中的所有函数(除 main 之外)都必须有原型吗?即使它们仅在同一翻译单元中定义后才使用?
To be truly standards-compliant, must all functions in C (except for main) have a prototype, even if they are only used after their definition in the same translation unit?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这取决于您所说的“真正符合标准”是什么意思。 然而,简短的答案是“确保所有函数在使用之前在范围内都有原型是一个好主意”。
一个更合格的答案指出,如果函数接受变量参数(特别是 printf() 系列函数),那么原型必须在严格符合标准的范围内。 对于 C89(来自 ANSI)和 C90(来自 ISO;除了节编号之外与 C89 相同)也是如此。 不过,除了 'varargs' 函数之外,返回
int
的函数不必声明,并且返回int
以外的值的函数确实需要一个声明来显示返回类型,但不需要参数列表的原型。但请注意,如果函数采用在没有原型的情况下受到“正常提升”的参数(例如,采用
char
或short
的函数 -两者都转换为int
;更严重的是,也许是一个采用float
而不是double
的函数),那么原型就是需要。 该标准对此很宽松,以允许旧的 C 代码在符合标准的编译器下进行编译; 编写较旧的代码并不是为了确保函数在使用前被声明 - 并且根据定义,较旧的代码不使用原型,因为在有标准之前它们在 C 中不可用。C99 不允许“隐式 int”...这意味着像“
static a;
”(默认情况下为int
)这样的奇怪情况以及隐式函数声明。 ISO/IEC 9899:1999 的前言中提到了这些内容(以及大约 50 个其他主要更改),将该标准与以前的版本进行比较:
在 ISO/IEC 9899:1990, §6.3.2.2 函数调用 中规定:
1999 年标准中缺少该段落。 我(还)没有跟踪在 C90 中允许
static a;
而在 C99 中不允许它(需要static int a;
)的措辞变化。请注意,如果函数是静态的,则可以在使用之前定义它,并且前面不需要声明。 如果定义非静态函数时前面没有声明 (
-Wmissing-prototypes
),GCC 可能会被说服 witter。It depends on what you mean by 'truly standards compliant'. However, the short answer is "it is a good idea to ensure that all functions have a prototype in scope before being used".
A more qualified answer notes that if the function accepts variable arguments (notably the
printf()
family of functions), then a prototype must be in scope to be strictly standards compliant. This is true of C89 (from ANSI) and C90 (from ISO; the same as C89 except for the section numbering). Other than 'varargs' functions, though, functions which return anint
do not have to be declared, and functions that return something other than anint
do need a declaration that shows the return type but do not need the prototype for the argument list.Note, however, that if the function takes arguments that are subject to 'normal promotions' in the absence of prototypes (for example, a function that takes a
char
orshort
- both of which are converted toint
; more seriously, perhaps, a function that takes afloat
instead of adouble
), then a prototype is needed. The standard was lax about this to allow old C code to compile under standard conformant compilers; older code was not written to worry about ensuring that functions were declared before use - and by definition, older code did not use prototypes since they did not become available in C until there was a standard.C99 disallows 'implicit int'...that means both oddball cases like '
static a;
' (anint
by default) and also implicit function declarations. These are mentioned (along with about 50 other major changes) in the foreword to ISO/IEC 9899:1999,which compares that standard to the previous versions:
In ISO/IEC 9899:1990, §6.3.2.2 Function calls stated:
This paragraph is missing in the 1999 standard. I've not (yet) tracked the change in verbiage that allows
static a;
in C90 and disallows it (requiringstatic int a;
) in C99.Note that if a function is static, it may be defined before it is used, and need not be preceded by a declaration. GCC can be persuaded to witter if a non-static function is defined without a declaration preceding it (
-Wmissing-prototypes
).原型是一个函数声明,它指定函数参数的类型。
Pre-ANSI C(Kernighan & Ritchie 的《The C 编程语言》1978 年第一版中描述的语言)没有原型; 函数声明不可能描述参数的数量或类型。 由调用者决定传递正确数量和类型的参数。
ANSI C 引入了“原型”,即指定参数类型的声明(从早期 C++ 借用的功能)。
从 C89/C90(ANSI 和 ISO 标准描述相同的语言)开始,调用没有可见声明的函数是合法的; 提供了隐式声明。 如果隐式声明与实际定义不兼容(例如,调用
sqrt("foo")
,则行为未定义。此隐式声明和非原型声明都不能与可变参数兼容函数,因此对可变参数函数(如 printf 或 scanf)的任何调用都必须具有可见原型,而对没有可见声明的函数的任何调用都是如此。约束违规,需要编译器诊断,但该声明仍然不需要是原型;它可以是不指定参数类型的旧式声明,
。
因此即使截至目前 2011 年 ISO C 标准中,旧式函数声明和定义(自 1989 年以来已“过时”)仍然允许在符合代码中使用,
对于可追溯到 1989 年的所有 C 版本,就风格而言,几乎没有。不为所有函数使用原型的原因只是为了避免破坏旧代码。
A prototype is a function declaration that specifies the types of the function's parameters.
Pre-ANSI C (the language described by the 1978 first edition of Kernighan & Ritchie's "The C Programming Language") did not have prototypes; it was not possible for a function declaration to describe the number or types of the parameters. It was up to the caller to pass the correct number and type of arguments.
ANSI C introduced "prototypes", declarations that specify the types of the parameters (a feature borrowed from early C++).
As of C89/C90 (the ANSI and ISO standards describe the same language), it's legal to call a function with no visible declaration; an implicit declaration is provided. If the implicit declaration is incompatible with the actual definition (say, calling
sqrt("foo")
, then the behavior is undefined. Neither this implicit declaration nor a non-prototype declaration can be compatible with a variadic function, so any call to a variadic function (likeprintf
orscanf
) must have a visible prototype.C99 dropped implicit declarations. Any call to a function without a visible declaration is a constraint violation, requiring a compiler diagnostic. But that declaration is still not required to be a prototype; it can be an old-style declaration that doesn't specify parameter types.
C11 made no significant changes in this area.
So even as of the 2011 ISO C standard, old-style function declarations and definitions (which have been "obsolescent" since 1989) are still permitted in conforming code.
For all versions of C going back to 1989, as a matter of style, there is very little reason not to use prototypes for all functions. Old-style declarations and definitions are kept only to avoid breaking old code.
不,函数并不总是需要原型。 唯一的要求是在使用函数之前先“声明”它。 声明函数有两种方法:编写原型,或编写函数本身(称为“定义”)。定义始终是声明,但并非所有声明都是定义。
No, functions do not always need a prototype. The only requirement is that a function be "declared" before you use it. There are two ways to declare a function: to write a prototype, or to write the function itself (called a "definition.") A definition is always a declaration, but not all declarations are definitions.
编写新函数时的一个很好的技巧是将它们颠倒编写,并将 main 放在底部,这样当您改变对函数的参数或返回类型的想法时,您不必也修复原型。 不断修复原型,并在编译器过时时处理所有警告,这真的很乏味。
一旦您的函数能够顺利地协同工作,请将代码移动到一个命名良好的模块中,并将原型放入同名的 .h 文件中。 它节省了大量的时间。 这是我 5 年来发现的最大的生产力辅助工具。
A nice tip when writing new functions is to write them upside-down with main at the bottom so when you change your mind about the function's args or return type you don't have to fix the prototype too. Constantly fixing prototypes, and dealing with all the compiler's warnings when they are out of date gets really tedious.
Once you have your functions working smoothly together move the code to a well-named module and put the prototypes in a .h file of the same name. It saves serious time. The biggest productivity aid I've found in 5 years.
是的,每个函数都必须有一个原型,但该原型可以出现在单独的声明中或作为函数定义的一部分。 用 C89 及更高版本编写的函数定义自然有原型,但如果您用经典的 K&R 风格编写内容,则:
那么函数定义没有原型。 如果您编写 ANSI C (C89) 风格,则:
那么函数定义就有原型。
Yes, every function must have a prototype, but that prototype may appear either in a separate declaration or as part of the function's definition. Function definitions written in C89 and up naturally have prototypes, but if you write things in classic K&R style, thus:
then the function definition has no prototype. If you write ANSI C (C89) style, thus:
then the function definition has a prototype.
据我所知(在 ANSI C89/ISO C90 中),没有。 我不确定C99; 然而,我也希望如此。
个人注意:我只在以下情况下编写函数原型...
To the best of my knowledge (in ANSI C89/ISO C90), no. I am unsure about C99; however, I would expect the same.
Personal Note: I only write function prototypes when...