我正在通过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;
}
答案 0 :(得分:7)
第一行是该函数的声明。底部的代码块是函数的定义。
从1999版ISO C标准开始,在没有可见声明的情况下调用函数是非法的(违反约束);声明必须在电话会议之前。
对于像这样的简单程序,您可以在power()
的定义之前编写main()
的完整定义(因为定义也提供了声明),但是对于更复杂的情况(例如作为递归调用,您经常需要提供单独的声明。
对于较大的程序,通常会在头文件(例如foo.h
)中收集所有函数声明,并在源文件中收集相应的定义({{1} }, 例如)。 foo.c
指令用于使声明在其他文件中可见。你会在书的后面看到这种事情。
(在1990年和早期版本的C中,这是K&amp; R2所涵盖的,有些情况下你可以在没有可见声明的情况下调用函数 - 但无论如何提供显式声明仍然是一个非常好的主意。)
顺便提一下,主程序的声明应该是#include "foo.h"
,而不仅仅是int main(void)
。
术语:“prototype”是一个函数声明,它指定参数的类型。
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)
不是。
非原型声明是“过时的”,这意味着它们理论上可以从未来的语言标准中删除。但它们自1989年以来已经过时,即使在新的2011 ISO C标准中,委员会也认为不适合将其删除。
答案 1 :(得分:2)
int power(int m, int n);
是原型形式的power
函数的声明。函数声明通知编译器函数具有的参数个数,函数参数的类型以及函数返回值的类型。
在C中,在声明之前不能使用函数标识符。
答案 2 :(得分:2)
这是一个转发声明,它使函数接口公开,因为在main()
下面实际实现之前使用了该函数。
头文件,你#include
提供了使可调用API公开的类似功能---但是代码通常是在库中提供的,而不是通过与此处单独的编译单元相同的 - K&amp; R简介章节的文件示例。
答案 3 :(得分:1)
如果您未在顶部包含该行,则尚未声明主程序中的程序到达power(2,i)
。程序从上到下读取,通过将声明放在顶部,编译器知道“定义即将到来”。
答案 4 :(得分:1)
该行只是功能原型。这是一个前向声明,它允许代码能够使用某些功能,当一切都链接在一起时,该签名将存在。没有它,main()
函数将尝试使用power()
函数,但编译器还没有意识到它,因为它实际上并未在源文件中实际定义。
答案 5 :(得分:0)
您所指的顶部的那一行是一个函数原型。它唯一的用途是编译器可以检查你的工作,也就是说,通过传递正确的类型和数量的参数来确保你正确使用该函数。这就是它的全部。这就是为什么你可以删除它并且代码仍然编译 - 你通过删除它所做的就是删除编译器的引用,这样它就无法检查你的工作。如果你删除它,那么你可能会传递错误类型的参数并导致很难找到运行时错误或程序崩溃。但是保留它允许编译器在编译时标记这样的错误,从而节省一些悲伤。拯救某人一些悲伤是件好事。
后来,根据C99标准,他们决定强制提供函数原型(或者首先定义函数)以便编译代码,从而迫使你让编译器检查你的工作。