【问题标题】:C++ - Calling a function inside the same function's definitionC++ - 在同一个函数的定义中调用一个函数
【发布时间】:2013-06-30 05:59:27
【问题描述】:

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

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

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

【问题讨论】:

    标签: c++ function recursion


    【解决方案1】:

    调用自身的函数称为递归函数。这是因为编译器只需要一个函数的声明,而不是它的定义,你就可以调用它。定义的第一行也用作声明。 (有关详细信息,请参阅 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 的维基百科页面也可能会有所帮助。

    【讨论】:

    • 我想补充一点,一般来说,您应该更喜欢使用迭代而不是使用递归。所以如果你想计算阶乘,你最好使用int result; for (int i=2; i&lt;=n; ++i) result *= i;,因为1)它工作得更快,因为调用一个函数不是免费的; 2) 如果你深入递归,你可能会得到(哈哈)堆栈溢出 :) 递归是一个非常强大的工具,没有它,许多算法都是毫无意义的。但请谨慎使用。
    【解决方案2】:

    这是因为您误解了“定义”函数的含义。

    首先让我们弄清语义:为了引用一个函数,您只需要它的声明,而不是它的完整实现

    通常,创建可执行文件有两个步骤: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 路递归函数时。

    【讨论】:

    • 你为什么说他误解了定义函数的含义?
    • 让我澄清一下,在 C/C++ 中定义一个函数通常被认为只是声明它。它不涉及实际提供功能的实现。但是,正常的英语用法将定义身体。模棱两可造成误解。实际上,由于#defines,这个词甚至不应该在 C/C++ 语言专有上下文中使用,而是应该限制在预处理器上下文中。
    • 这根本不正确。 C++ 标准和普通 C++ 术语明确区分了声明和定义函数,并且这些定义与 OP 的用法相对应。我建议您阅读标准的§§ 8.3.58.4
    • @Lstor 你认为他明白定义一个函数会隐式声明它吗?
    • @ColorlessPhoton 请参阅stackoverflow.com/questions/1410563/… 以获取有关声明与定义的进一步说明。就像我在回答中指出的那样,定义中的第一行也是一个声明。关于 One Definition Rule 的 Wikipedia 页面也是相关的。 en.wikipedia.org/wiki/One_Definition_Rule
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 2020-10-04
    • 2014-08-01
    相关资源
    最近更新 更多