5

我正在编写类似于下面的代码的内容,但我不小心在函数定义的主体内调用了相同的函数。

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

我什至在“完成”定义它之前就调用了该函数。然而,它有效。为什么?

4

2 回答 2

13

调用自身的函数称为递归函数。这是有效的,因为编译器只需要函数的声明,而不是它的定义,就可以调用它。定义的第一行也用作声明。(有关详细信息,请参阅C++11 标准的第 8.4.1.2 节。)

递归非常适合解决许多问题。递归函数的典型例子是factorial函数。它被定义为factorial(n) = n * factorial(n-1)

您可以尝试运行这段代码,以进一步了解函数调用自身时会发生什么:

#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);
}

有关声明与定义的更多信息,请参阅此答案。关于单一定义规则的维基百科页面也可能会有所帮助。

于 2013-06-30T06:01:32.823 回答
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 路递归函数时。

于 2013-06-30T06:22:27.210 回答