为什么main()之后有函数定义?

时间:2011-05-27 12:09:36

标签: c unix open-source

我认为最权威的程序员之一(Richard Stallman)编写的最着名的操作系统(linux)中最常用的系统函数之一(ls)可能是一个写得很好的例子码。

因此,作为开源,我决定查看代码(参见例如here)。在那里我发现了几个在main()之后定义的函数,因此在他们调用之后,我预计这种函数非常罕见。

有经验的C程序员会对此发表评论吗?

5 个答案:

答案 0 :(得分:9)

Stallman在这里做的事情绝对没有错。

C语言允许之后将定义的函数的forward declaration

这有许多优点,不应被视为不良行为,而是非常好的行为。

优点(并非详尽无遗):
- 让程序员快速了解C代码公开的API,而无需查看所有代码 - 允许使用头文件,其中声明稍后将在编译过程中定义的函数。因此,每次使用时都不必定义函数。

在这个ls实现的情况下,他只是预先声明了他将在main()中使用的函数,但如果仔细观察,主要函数是第一个出现的函数。 这很可能是出于可读性的考虑,因此您无需一直向下滚动即可到达程序的入口点。

请注意,词汇在这里很重要:
- 函数声明意味着:只告诉编译器,在代码的某处,将定义一个具有相同名称的函数。
- 函数定义:实际的函数实现

int my_function( char *text); // function declaration, no implementation
int main( int argc, char **argv)
{
   return my_function(argv[0]); // use of the declared function
}

// actual function definition / implementation
int my_function( char *text )
{
   printf("%s\n", text);
}

编辑:仔细查看代码后,您可以看到Stallman没有向前声明他的所有功能。他还有一种相当奇怪的定义函数的方式。我将这归因于代码的旧版本,该代码的日期是1985年,当时C编译器的定义不如今天。 在声明或定义之前,它必须允许这种函数用法

最后但并非最不重要的是,最新版本的ls源代码可在此处找到:http://coreutils.sourcearchive.com/documentation/7.4/ls_8c-source.html
与'85(Back-to-the-Future)版本相比,它具有更多符合C99标准的编码。

答案 1 :(得分:4)

在C中,您通常在头文件中定义function prototypes,然后包含头文件,因此可以在调用函数后安全地定义函数。

在您提供的示例文件中,程序足够小,原型只是在程序声明之前放在文件的顶部,但同样的原则适用。

编辑:此外,该文件是pre-K& R C,这有点酷,但与现代C有一些显着差异,你不一定要模仿它。

答案 2 :(得分:2)

代码将在概念上阅读,就像一个很好的规范

  • 首先定义主入口点使用的接口
  • 然后定义主要入口点
    • 然后在概念上完全指定程序在理想情况下的行为。
  • 最后,实现界面(main之后的定义)。

他忽略了在main之前放置的函数的声明(以满足第一个子弹 - 定义接口)被许多程序员认为是关于现代C的坏风格。

然而,代码使用旧式函数定义,其声明不会为调用者定义参数类型。据推测,将此代码更新为现代C会破坏许多仍使用ANSI前编译器的非常旧的系统。

答案 3 :(得分:2)

对我而言,这是一个来自原型前时代的遗物,看看更新的代码,你常常会看到一个倒置的代码顺序。我的主要缺点是你必须在顶部声明函数,然后函数本身向下。如果更改功能的签名,则还必须在2个位置进行更改。请注意,静态函数的情况也是如此,这部分代码中根本没有使用的功能...... 如果你不这样做,肯定会编译,但你会被C的隐含声明早先或稍后咬过。

说真的,找到一些比一些oldskool K& R样式C更新的代码,从那时起C语言发展很快,你可能会在这个过程中找到一些坏习惯。

答案 4 :(得分:1)

在调用函数之前,您需要在范围内拥有原型。函数定义用作原型

/* this is a definition and a prototype */
int fx1(void) {
    return 42;
}

/* this is only a prototype */
int fx2(int, const char*);

int main(void) {
    fx1(); /* ok, prototype in scope */
    fx2(42, "foobar"); /* ok, prototype in scope */
}

/* fx2 definition.
** it is undefined behaviour if the prototype here
** does not match the previous prototype */
int fx2(int k, const char *t) {
    return strlen(t) - k;
}

通常,原型在头文件中声明,头文件包含在实现文件的顶部,在任何代码之前。

#include "prototypes.h"
/* define functions in any order: prototypes are all in scope */