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

时间:2010-04-04 16:59:35

标签: c function function-prototypes

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

10 个答案:

答案 0 :(得分:64)

永远不需要为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;
}

在C99中,您调用的每个函数必须在调用之前声明。但是,仍然没有必要专门用原型声明它。非原型声明也可以使用。这意味着在C99中,“隐式int”规则不再起作用(对于推断函数返回类型,在本例中),但如果声明函数没有原型,则仍可以从参数类型中猜出参数类型。 / p>

前面的示例不会在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。

答案 1 :(得分:59)

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

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

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

约定总是在头文件中声明一个原型,该头文件与包含该函数的源文件同名。

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

答案 2 :(得分:6)

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

答案 3 :(得分:2)

这不是必需的,但不使用原型是不好的做法。

使用原型,编译器可以验证您是否正确调用了函数(使用正确的数字和参数类型)。

没有原型,就可以拥有这个:

// 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);
}

答案 4 :(得分:2)

在C中,如果我们不声明函数原型并使用函数定义,则没有问题,如果函数的返回类型是“整数”,程序将编译并生成输出。在所有其他条件下,将出现编译器错误。原因是,如果我们调用一个函数并且没有声明函数原型,那么编译器会生成一个原型,它返回一个整数并搜索相似的函数定义。如果函数原型匹配则编译成功。如果返回类型不是整数,则函数原型不匹配,并且它会生成错误。因此最好在头文件中声明函数原型。

答案 5 :(得分:1)

C允许函数被调用,即使它们之前没有被声明,但我强烈建议你在使用之前声明所有函数的原型,以便编译器可以在你使用错误的参数时保存它们。

答案 6 :(得分:1)

您应该将函数声明放在头文件(X.h)中,将定义放在源文件(X.c)中。然后其他文件可以#include "X.h"并调用该函数。

答案 7 :(得分:1)

根据C99标准,函数原型不是强制性的。

答案 8 :(得分:0)

声明调用代码的函数并不是绝对必要的。但有一些警告。假定未声明的函数返回int,编译器将首先发出有关未声明函数的警告,然后发出有关返回类型和参数类型不匹配的警告。

说过很明显,用原型正确地声明函数是一种更好的做法。

答案 9 :(得分:-4)

选择“选项”菜单,然后选择“编译器” C ++ 选项。在弹出的对话框中,选择“CPP always” 在“使用C ++编译器”选项中。 再次选择“选项”菜单,然后选择“环境|” 编辑'。确保默认扩展名为“C”而不是 “CPP”。