2

当我浏览书籍和网站时,写到函数声明/原型是必须的,但是当我运行我的程序而不声明它时,它也给了我正确的结果。

你能解释一下为什么吗?

4

7 回答 7

7

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

函数声明很重要,因为调用约定(平台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();

现在这可能会弄乱堆栈,因为函数将返回值压入堆栈,但赋值不会弹出它,因为它假定一个整数返回值。

于 2012-12-28T09:05:28.447 回答
3

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

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

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

如果您使用 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
...
于 2012-12-28T09:17:29.237 回答
2

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

在很多情况下,您别无选择,否则代码将很难维护。

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

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

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

于 2012-12-28T09:01:14.930 回答
0

只有在这种情况下,当您在代码中使用此函数时,才必须声明函数的原型,然后再对其进行描述。

于 2012-12-28T09:00:55.747 回答
0

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

于 2012-12-28T09:01:35.670 回答
0

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

仅当函数调用在函数实现之前才需要。

向编译器提及此处声明的函数已在程序中某处使用是一个好习惯。

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

于 2012-12-28T11:12:33.283 回答
0

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

于 2012-12-28T08:59:40.900 回答