必须在C中声明函数原型吗?

Moh*_*nde 54 c function function-prototypes

我是C的新手(我之前有Java,C#和一些C++经验).在C中,是否有必要声明一个函数原型,或者代码可以在没有它的情况下编译?这样做是很好的编程习惯吗?或者它只是依赖于编译器?(我正在运行Ubuntu 9.10并在Code :: Blocks IDE下使用GNU C编译器或gcc)

AnT*_*AnT 65

永远不需要在C中声明函数的原型,既不在"旧"C(包括C89/90)也不在新C(C99)中.但是,在功能声明方面,C89/90和C99之间存在显着差异.

在C89/90中,根本没有必要声明一个函数.如果函数未在调用时声明,则编译器会从调用中传递的参数类型中隐式地"猜测"(推断)声明,并假定返回类型为int.

例如

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;
}
Run Code Online (Sandbox Code Playgroud)

在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;
}
...
Run Code Online (Sandbox Code Playgroud)

最终得到有效的C99代码.

然而,在调用函数之前声明函数的原型始终是一个好习惯.

另外一点:我上面说过,永远不需要声明一个函数原型.事实上,对于某些功能来说,这是一项要求.为了在C中正确调用可变参数函数(printf例如),必须在调用点之前使用原型声明函数.否则,行为未定义.这适用于C89/90和C99.

  • @jfs GCC 文档附带帮助:“(...) 要获得标准所需的所有诊断信息,您还应该指定 `-pedantic`(如果您希望它们是错误而不是警告,则应指定 `-pedantic-errors` ) (...)”。 (3认同)
  • 详细解答+1,所有细微差别解释得很好 (2认同)

use*_*405 62

在ANSI C(意思是C89或C90)中,您不必声明函数原型; 但是,使用它们是最佳实践.标准允许您不使用它们的唯一原因是为了向后兼容非常旧的代码.

如果您没有原型,并且调用了函数,编译器将根据您传递给函数的参数推断出原型.如果稍后在同一个编译单元中声明该函数,如果函数的签名与编译器猜测的不同,则会出现编译错误.

更糟糕的是,如果函数在另一个编译单元中,则无法获得编译错误,因为没有原型就无法检查.在这种情况下,如果编译器出错,如果函数调用在堆栈上推送的函数不同于函数预期,则可能会得到未定义的行为.

惯例是始终在头文件中声明一个原型,该头文件与包含该函数的源文件同名.

在C99或C11中,标准C在调用任何函数之前需要在范围内进行函数声明.许多编译器在实践中不强制执行此限制,除非您强制它们这样做.

  • 在 C99 中,您确实需要一个函数声明 - 尽管我认为它不必是原型声明。C90 (C89) 对此松懈得多。请注意,如果函数使用“可变参数”,即使在 C89/C90 中,您也确实需要范围内的原型。 (2认同)
  • @JeremyP:默认情况下编译器仍然(太?)宽容,主要是因为仍然需要更新遗留代码库.编译器没有强制执行标准的字母,除非你强迫他们的手(例如,`-Werror`,也许是`-pedantic`或`-Wold-style-definition`或`-Wold-style-declaration`或`-Wmissing-prototypes`或`-Wstrict-prototypes`或以上所有(或大部分).我通常使用`-std = c11`以及除`-pedantic`之外的所有内容,但我确保POSIX或相关的`#define`是活动的.我不经常写`#define _GNU_SOURCE`或`#define _BSD_SOURCE`. (2认同)

Dra*_*sha 6

如果在使用之前定义了函数,那么它不是必须的.