我有一些Java、C#和C++的经验,但对于C语言还比较新。在C语言中,声明函数原型是否必要?如果不进行声明,代码是否能够编译通过?在编程实践中,声明函数原型是好的习惯吗?还是这取决于编译器?(我正在运行Ubuntu 9.10,并在Code::Blocks IDE下使用GNU C编译器gcc)
我有一些Java、C#和C++的经验,但对于C语言还比较新。在C语言中,声明函数原型是否必要?如果不进行声明,代码是否能够编译通过?在编程实践中,声明函数原型是好的习惯吗?还是这取决于编译器?(我正在运行Ubuntu 9.10,并在Code::Blocks IDE下使用GNU C编译器gcc)
int main() {
int i = foo(5);
/* No declaration for `foo`, no prototype for `foo`.
Will work in C89/90. Assumes `int foo(int)` */
return 0;
}
int foo(int i) {
return i;
}
C99规定,在调用函数之前必须先进行声明。然而,这并不一定要用原型进行声明。非原型的声明同样有效。这意味着在C99中,“隐式 int
” 规则不再适用于函数的推断返回类型(在这种情况下),但如果函数没有使用原型声明,参数类型仍然可以从参数类型进行猜测。
上述示例代码在C99中将无法编译,因为在调用处未声明foo
函数。但是,你可以添加一个非原型的声明。
int foo(); /* Declares `foo`, but still no prototype */
int main() {
int i = foo(5);
/* No prototype for `foo`, although return type is known.
Will work in C99. Assumes `int foo(int)` */
return 0;
}
...
最终将得到有效的C99代码。
然而,在调用函数之前声明函数原型始终是一个好习惯。
另外需要注意的是:我上面说过,从来不需要声明函数原型。实际上,对于某些函数,这是必需的。为了正确调用C语言中的可变参数函数(例如printf
),必须在调用点之前声明该函数并带有原型。否则,行为是未定义的。这适用于C89 / 90和C99。
-pedantic
(或者如果您想将它们变成错误而不是警告,则使用 -pedantic-errors
)(...)」。 - Przemek-pedantic
选项会导致gcc打印违反约束和语法规则所需的诊断信息。在某些情况下,这些诊断信息仅仅是警告——并且很难区分这些警告和语言不要求的其他警告之间的区别。将-pedantic
替换为-pedantic-errors
,可以使gcc将语言违规视为致命错误。” - Rick-Werror
,也许还有 -pedantic
或 -Wold-style-definition
或 -Wold-style-declaration
或 -Wmissing-prototypes
或 -Wstrict-prototypes
或以上所有(或大部分)),否则编译器不会强制执行标准。我通常使用 -std=c11
和所有上述选项,但我确保 POSIX 或相关的 #define
处于活动状态。我很少编写 #define _GNU_SOURCE
或 #define _BSD_SOURCE
。 - Jonathan Leffler如果函数在使用之前已经被定义,那么这并不是必须的。
虽然不是必需的,但不使用原型是一种不好的实践。
使用原型可以让编译器验证您是否正确调用函数(使用正确数量和类型的参数)。
如果没有原型,则可能会出现这种情况:
// file1.c
void doit(double d)
{
....
}
int sum(int a, int b, int c)
{
return a + b + c;
}
并且这个:
// file2.c
// In C, this is just a declaration and not a prototype
void doit();
int sum();
int main(int argc, char *argv[])
{
char idea[] = "use prototypes!";
// without the prototype, the compiler will pass a char *
// to a function that expects a double
doit(idea);
// and here without a prototype the compiler allows you to
// call a function that is expecting three argument with just
// one argument (in the calling function, args b and c will be
// random junk)
return sum(argc);
}
// file1.c
和// file2.c
的意思是这些是不同的文件。当编译file2.c时,编译器将没有看到file1.c中的定义。 - R Samuel KlatchkoC语言允许调用尚未声明的函数,但我强烈建议在使用函数之前先声明所有函数的原型,以便编译器在您使用错误参数时进行提示。
你应该将函数声明放在头文件(X.h)中,将定义放在源文件(X.c)中。然后其他文件可以#include "X.h"
并调用该函数。
根据 C99
标准,函数原型不是强制性的。
对于调用代码编译而言,声明函数并非绝对必要的。但是有一些注意事项需要注意。未声明的函数被假定返回int
类型,并且编译器会首先发出有关函数未声明的警告,然后再针对返回类型和参数类型不匹配进行警告。
尽管如此,正确声明带有原型函数的做法显然更好。