我正在编写类似于下面代码的东西,我不小心在函数定义的主体内部调用了相同的函数。
double function(double &value)
{
//do something with a here
if(some condition)
{
function(a);
}
return a;
}
考虑一下这种形式:
int function(int &m) {
m = 2*m;
if(m < 20)
{
function(m);
}
return m;
};
int main() {
int a = 2;
std::cout <<"Now a = "<<function(a);
return 1;
}
据我说,这应该不运行更不用说编译了。但它确实运行并给出了正确的结果
现在a = 32
在我完成定义之前,我已经调用了该函数。然而,它有效。为什么呢?
答案 0 :(得分:12)
调用自身的函数称为递归函数。这是有效的,因为编译器只需要函数的声明,而不是它的定义,因为你可以调用它。定义的第一行也作为声明。 (有关详细信息,请参阅C ++ 11标准的§8.4.1.2。)
递归非常适合解决许多问题。递归函数的典型示例是factorial
函数。它被定义为factorial(n) = n * factorial(n-1)
。
您可以try running this piece of code更多地了解函数调用自身时会发生什么:
#include <iostream>
int factorial(unsigned int n)
{
std::cout << "Computing factorial of " << n << "\n";
int result;
if (n == 0) {
result = 1;
} else {
result = n * factorial(n-1);
}
std::cout << "factorial(" << n << ") = " << result << "\n";
return result;
}
int main()
{
factorial(5);
}
有关声明与定义的更多信息,请参阅this answer。关于One Definition Rule的维基百科页面也可能有所帮助。
答案 1 :(得分:3)
这是因为你误解了一个“定义”一个函数意味着什么。
首先让我们得到正确的语义:为了引用一个函数,你只需要它的声明而不是它的完整实现。
通常,创建可执行文件有两个步骤:1)编译和2)链接。
编译步骤确定每个部分的语法对于每个函数都是可以的。为了编译,您需要核心语言关键字或正确声明的符号。编译器将这些放在一起但不解决这些符号是否实际指向可执行文件中的真实对象。对象是数据位或指令位。通常,这些blob在目标文件中称为数据段和文本段。
在链接步骤中,仍然使用符号识别的每个blob被放在一起,以查看代码中的所有引用是否都有相应的blob。
现在你已经知道所有你需要评估语法是否正确(编译步骤)的声明。事实上,你在函数体内有一个符号引用,这根本不是问题。
您正在做的事情是以下方面的简写:
int function(int &m); // implicit declaration
int function(int &m) {
m = 2*m;
if(m < 20)
{
function(m); // this only requires the declaration to be accepted
// by the compiler.
}
return m;
};
int main() {
int a = 2;
std::cout <<"Now a = "<<function(a);
return 1;
}
这种能力非常重要,因为它是创建递归函数的基础。使用递归比使用迭代更优雅地解决了一些问题,特别是当你开始抛出n路递归函数时。