功能声明有多重要?

时间:2012-12-28 08:55:30

标签: c function

当我浏览书籍和网站时,会写出函数声明/原型是必须的,但是当我在没有声明的情况下运行我的程序时,它也会给我正确的结果。

你能解释一下原因吗?

7 个答案:

答案 0 :(得分:6)

(我说的是函数的定义不在调用它的函数之前的情况,因此函数定义不能提供有关原型的信息。如果不是这样,那么声明函数是这不是强制性的,但它仍然被认为是一种良好的做法,因为它不依赖于函数定义的顺序,并且当一个人改变这个顺序时它不易出错。)

函数声明很重要,因为calling convention(平台的ABI的一部分)可以为参数定义不同的位置,并根据函数返回并作为参数接受的类型返回值。例如,返回(足够大)结构的函数的返回值通常放在堆栈上,而返回int的函数通常使用CPU寄存器作为返回值。没有函数原型,大多数编译器都假设函数返回int,这可能是错误的。请考虑以下示例:

// in a separate implementation file
struct foo {
    long long bar;
    char buf[128];
};

struct foo function()
{
    struct foo f = { 0 };
    return f;
}

// in another file - the implementation is invisible here,
// and in the lack of a prototype, the compiler assumes an integer return value
int x = function();

现在这可能会使堆栈陷入困境,因为该函数会在堆栈上推送返回值,但是赋值不会弹出它,因为它假设一个整数返回值。

答案 1 :(得分:3)

在某种程度上,它取决于您的代码符合哪种标准。预标准编译器非常宽松,因为它们根本不支持原型。 C89标准引入了原型,但必须允许已经存在的大量代码进行编译。因此,原型是可选的(技术上除了支持可变数量的参数的printf()之类的函数;对于那些,原型在C89中是强制性的,即使编译器通常不会抱怨 - 或者导致麻烦 - 如果声明丢失了。)

使用C99(和C2011),在使用函数之前,您应该可以看到每个函数的原型声明。在定义之前,每个非静态函数的原型声明都是可见的,这是一个好主意。该声明应该在一个标题中,其他使用该函数的文件可以包含在该标题中。标题充当“裁判”;它确保使用该函数的代码具有正确的声明信息,并且定义该函数的代码与其他所有使用的声明匹配。

通常,除非您将编译器推入严格模式,否则它将努力接受程序。向GCC添加-std=c99 -pedantic -Wall -Wextra等选项将启用大量警告。值得让你的代码在这些选项下干净利地编译。

如果您使用GCC,您可以添加警告更多选项,以确保您收到遗漏通知。根据GCC的版本,您可以使用:

  • -Wmissing-prototypes(如果没有原型警告你)
  • -Wstrict-prototypes(警告您关于非原型,例如extern int func();
  • -Wold-style-declaration(警告你'旧式',K& R,非原型声明)
  • -Wold-style-definition(警告你关于'旧式',K& R,非原型定义)

我一直在使用前两个;所有GCC 4.x甚至3.x系列编译器都支持它们。后两者我在过去一年左右才遇到过; GCC 4.7.x支持它们,但GCC 4.1.x不支持它们(4.5.1支持它们;这是我手头上4.1.2以外的最旧版本)。

GCC 4.7.x有一个有用的选项:gcc --help=warnings。这给出了它支持的所有警告的列表,并带有简单的摘要:

...
-Wmissing-prototypes        Warn about global functions without prototypes
...
-Wold-style-declaration     Warn for obsolescent usage in a declaration
-Wold-style-definition      Warn if an old-style parameter definition is used
...
-Wstrict-prototypes         Warn about unprototyped function declarations
...

答案 2 :(得分:2)

使用函数声明/原型是一个非常好的练习/设计(当你不需要时)。

在很多情况下,你没有选择,或者代码很难维护。

  • 如果程序只有一个.c文件,如果你不使用原型,你必须按正确的顺序将函数命令到.c文件中

    =>在描述之前在代码中使用函数时,必须声明函数的原型。

  • 当您需要编写更大的程序时,您将把代码分解为单独的模块。 每个模块都有“公共”和“私人”功能。后者将被声明为static(您仍然需要在.c文件的顶部添加原型),而'public'函数原型将成为.h文件(标题)

答案 3 :(得分:0)

只要函数实现位于函数调用之前,代码就可以在没有单独的函数声明的情况下工作,因为函数已经由实现本身声明了。 通常 - 特别是对于largre程序 - 你应该为任何函数做一个声明。

答案 4 :(得分:0)

只有在这种情况下,当你在之前的代码中使用这个函数时,必须声明一个函数的原型。

答案 5 :(得分:0)

函数声明对于让编译器尽快检测数据类型问题很有用。如果您创建一个需要float的函数,而不是传递char,它可能在某些情况下仍然有效(例如用于比较),并且当您开始在其上构建代码时就会崩溃。最后,计算机将所有内容表示为位数组,因此您可以“加”两个字符或连接包含\0的两个整数,就好像它们是字符串一样,但它会使程序变得混乱,无法维护和调试未来。

答案 6 :(得分:0)

函数声明(也称为前向声明)是必需的,因为程序的编译是从上到下完成的(即从第1行到最后一行)..

只有当函数调用在函数实现之前时才需要它。

这是一个很好的做法,提到编译器,这里声明的函数已在程序中的某处使用过。

声明也可以在用户定义的头文件中完成..