【问题标题】:Pointers in C with recursionC中带有递归的指针
【发布时间】:2014-10-10 06:21:47
【问题描述】:

我经常用java编程,最近看了一些c代码。 我遇到了这个程序,但我不知道这个指针是如何工作的。 我知道指针存储地址,但无法通过程序。 请告诉输出 8 是怎么来的?

#include <stdio.h>

int fun(int n, int * f_p) {
    int t, f;
    if (n <= 1) {
      *f_p = 1;
      return 1;
    }
    t = fun(n - 1, f_p);
    f = t + *f_p;
    *f_p = t;
    return f;
}

int main() {
    int x = 15;
    printf("%d\n", fun(5, &x));
    return 0;
}

【问题讨论】:

  • 在调试器下运行并单步执行递归。
  • 这太可怕了
  • @philippe lhardy:当您编写一个需要大量中间临时数据的递归函数时,这些数据不需要保存为本地上下文和/或嵌套递归调用,那么这实际上是你应该怎么做。在调用者中创建一个“全局”上下文,并通过指针参数将其传递给所有递归级别。这样,所有递归级别都将使用相同的“全局”数据,而不是在每个递归级别上一次又一次地在本地实例化该数据。这种技术将允许您减少堆栈使用并避免堆栈溢出。
  • 但是,在这种情况下,它用于不同的目的:将第二个值从较低级别返回到较高级别。因此,它本身并不是真正的“上下文”。没有真正需要在所有递归级别使用相同的 x 变量。
  • @AndreyT 我同意你的两个 cmets。我仍然坚持这样一个事实,即代码中的两行 cmets 可能会保存一个 SO 帖子:-)。

标签: c pointers recursion


【解决方案1】:

这里有一个递归函数,它计算斐波那契数列的第 i 个元素(索引来自 0)。每次递归迭代都会返回两个值:第 i 个斐波那契数和第 (i-1) 个(前一个)斐波那契数。由于 C 中的函数只能返回一个值(好吧,除非您使用结构体作为返回类型),因此另一个值 - 前一个斐波那契数 - 通过指针参数 f_p 返回给调用者。

所以,当你调用fun(5, &amp;x)时,该函数将返回8,即第5个斐波那契数,它还将5放入x,即前一个(第4个) ) 斐波那契数。

注意x 的初始值无关紧要。 15 在这个程序中没有任何作用。显然它是一个红鲱鱼。

如果您知道斐波那契数列是什么,您就知道数列的下一个元素是前两个元素的和。这就是编写函数以将序列的两个元素“返回”给调用者的原因。您可能不关心顶级调用者中的先前值(即main),但嵌套递归调用确实需要它来计算下一个数字。其余的都很简单。

【讨论】:

    【解决方案2】:

    一步一步:

    1. fun 使用 5x 地址调用
    2. fun 调用 fun 带有 4f_p,即 x 地址
    3. fun 调用 fun 带有 3f_p,即 x 地址
    4. fun 调用 fun 带有 2f_p,即 x 地址
    5. fun 调用 fun 带有 1f_p,即 x 地址
    6. fun 被调用为 1,因此 if 条件为真,将 1 放入 f_p(x) 指向的变量中并 返回 1强>
    7. 这个返回值赋值给fun(2,f_p)t,f为f = t + *f_p即1+1 -> f=2; f_p 指向的变量设置为 t 所以 x=1,返回 f 所以它返回 2
    8. 这个返回值赋值给fun(3,f_p)t,f为f = t + *f_p即2+1 -> f=3; f_p 指向的变量设置为 t 所以 x=2,返回 f 所以它返回 3
    9. 这个返回值赋值给fun(4,f_p)t,f为f = t + *f_p即3+2 -> f=5; f_p 指向的变量设置为 t 所以 x=3,返回 f 所以它返回 5
    10. 这个返回值赋值给fun(5,f_p)(第一次调用fun)的t,f是f = t + *f_p,即5+ 3 -> f=8; f_p 指向的变量设置为 t 所以 x=5,返回 f 所以它返回 8,这就是 printf 打印的内容

    【讨论】:

      【解决方案3】:

      另一个答案揭示了使用返回额外值的有用技术来计算斐波那契数。我以我认为更易于理解和维护的方式重写了代码。希望这可以防止人们认为你需要编写糟糕的代码来做这样的事情

      #include <stdio.h>
      
      int fib(int n) {
          // This is used to return the previous fib value
          // i.e. fib(n - 1)
          int prevValRet;
          return fibRec(n, &prevValRet);
      }
      
      // *prevValRet contains fib(n-2)
      int fibRec(int n, int *prevValRet) {
          // Termination case
          if (n <= 1) {
            // return fib(0) and fib(1) as 1
            *prevValRet = 1;
            return 1;
          }
          // Calculate fib(n-1)
          int prevVal = fibRec(n - 1, prevValRet);
          // Calculate fib(n) = fib(n-1) + fib(n-2)
          int thisVal = prevVal + *prevValRet;
          // Return fib(n-1) and fib(n)
          *prevValRet = prevVal;
          return thisVal;
      }
      
      int main() {
          printf("%d\n", fib(5));
          return 0;
      }
      

      【讨论】:

        【解决方案4】:

        随着这些事情的发展,它在技术上很简单,但是......愚蠢,因为没有人应该做这样的事情。考虑到副作用,这是对递归的不好使用和写得不好的递归。

        fun(5, &amp;x) 的原始调用不会触发条件。因此,它将递归四次(5-1、4-1、3-1、2-1)。这是您的基本条件,其作用是将指向的位置(原始的x)设置为1 并返回1

        然后我们展开四个调用,每次将返回值添加到指针处的事物将指针处的事物更改为总和。

        用简单的英语来说,你是加倍一三倍。

        编辑:正如所指出的,我将代码误读为将f 分配给*f_p 而不是t。这使它成为斐波那契计数器。

        【讨论】:

        • 什么是“你一倍三倍”的意思?
        • 它计算斐波那契,没有加倍 - 它会添加 1 + 1 = 2 然后 1 + 2
        • @PeterR,你是对的。我划分了t和f。还有更多错误代码的证据。
        • @AndreyT,将*f_p = t; 误读为*f_p = f;。这将返回 1,然后通过将值添加到自身然后将其存储在指针处来将其翻倍。
        猜你喜欢
        • 2018-05-01
        • 2017-07-23
        • 1970-01-01
        • 2014-05-10
        • 2014-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多