3

我正在通过 K&R 学习编程。到目前为止进展顺利,但我不清楚第 1.8 节(函数)中的一行代码的作用。

在 1.8 节中,作者向您展示了如何创建一个函数来将一个整数乘以另一个整数的幂。

我已经粘贴了下面的代码,因为它是在书中写的。一切都很好。但我不知道他们为什么将这一行放在顶部:

int power(int m, int n);

书中没有提到它,只是说程序会将整数 m 提高到 n 次方。如果我从代码中删除该行,程序仍会按应有的方式输出。

如果我理解正确的话,这条线

int power(int base, int n)

创建函数,下面的大括号定义函数。然后main下的大括号调用函数输出图表。

所以这一切似乎都说得通。但我看不到最上面的一行是做什么的。

这可能是无关紧要的,但我似乎更有可能遗漏了一些东西。谁能告诉我为什么那条线在那里?

#include <stdio.h>

int power(int m, int n);

/* test power function */
main()
{
int i;

    for (i = 0; i < 10; ++i)
        printf("%d %d %d\n", i, power(2,i), power(-3, i));
    return 0;
}

/* power: raise base to n-th power; n >= 0 */

int power(int base, int n)
{
    int i, p;

    p = 1;
    for (i = 1; i <= n; ++i)
        p = p * base;
    return p;
}
4

6 回答 6

7

第一行是函数的声明。底部的代码块是函数的定义

从 1999 版本的 ISO C 标准开始,在没有可见声明的情况下调用函数是非法的(违反约束);声明必须在调用之前。

对于像这样的简单程序,您可以在定义power()之前编写完整的定义main()(因为定义也提供声明),但对于更复杂的情况(例如递归调用),您通常需要提供单独的声明。

对于较大的程序,通常将所有函数声明收集在头文件(foo.h例如 )中,并将相应的定义收集在源文件中(foo.c例如 )。指令用于#include "foo.h"使声明在其他文件中可见。你会在本书后面看到这种东西。

(在 K&R2 涵盖的 1990 年和更早版本的 C 中,在某些情况下您可以在没有可见声明的情况下调用函数——但无论如何提供显式声明仍然是一个非常好的主意。)

顺便说一句,主程序的声明实际上应该int main(void)不仅仅是main().

术语:“原型”是指定参数类型的函数声明。

int power(int base, int n);    /* a declaration that's also a prototype */
int power(int, int);           /* likewise */
int power();                   /* a declaration but not a prototype */

(参数名称在定义中是必需的,但在独立声明中是可选的。)

作为一种特殊情况,没有参数的函数原型使用(void),因为空括号已经意味着非原型声明。int main(void)原型也是如此,但int main()不是。

非原型声明是“过时的”,这意味着它们理论上可以从未来的语言标准中删除。但它们自 1989 年以来就已经过时,即使在 2011 年新的 ISO C 标准中,委员会也认为不适合删除它们。

于 2012-01-11T21:55:38.343 回答
2
int power(int m, int n);

power函数原型形式的声明。函数声明通知编译器函数具有的参数数量、函数参数的类型和函数返回值的类型。

在 C 中,您不能在声明之前使用函数标识符。

于 2012-01-11T21:52:22.977 回答
2

这是一个前向声明,它使函数接口公开,因为函数在下面实际实现之前被使用main()

头文件,您#include提供了与公开可调用 API 类似的功能——但代码通常在库中提供,而不是通过与 K&R Intro 章节的单文件示例中相同的编译单元提供.

于 2012-01-11T21:52:58.117 回答
1

如果您没有在顶部包含该行,则power(2,i)尚未声明程序何时到达主电源。程序是从上到下读取的,通过将声明放在顶部,编译器就知道“定义即将到来”。

于 2012-01-11T21:52:42.733 回答
1

那行只是函数原型。这是一个前向声明,允许代码能够使用具有该签名的某些函数,该签名将在所有内容链接在一起时存在。没有它,该main()函数将尝试使用该power()函数,但编译器还不知道它,因为它实际上并没有在源文件中稍后定义。

于 2012-01-11T21:53:14.840 回答
0

您所指的顶部那一行是函数原型。它唯一的作用是让编译器可以检查您的工作,即通过传递正确的类型和数量的参数来确保您正确使用该函数。这就是它的全部目的。这就是为什么您可以删除它并且代码仍然可以编译的原因 - 您通过删除它所做的只是删除编译器的引用,因此它无法检查您的工作。如果您确实删除了它,那么您可能会传递错误类型的参数并导致难以发现的运行时错误或程序崩溃。但是将其保留允许编译器在编译时标记此类错误,从而为您节省一些麻烦。拯救某人一些悲伤是一件好事。

Later, with the C99 standard, they decided to make it mandatory to provide a function prototype (or else define the function first) in order for the code to compile, thus forcing you to let the compiler check your work.

于 2012-01-12T08:13:21.040 回答