1

我很难理解这个反向函数是如何工作的。我已经尝试在纸上一步一步地弄清楚代码的作用,但这对我来说没有意义。我对代码所做的最好(尽管很粗略)的解释是:

http://s7.postimg.org/632xhovwr/recursion_confusion.png

#include <stdio.h>
#include <string.h>
#define MAX 1000

void reverse(char s[]);

main()
{
    char str[] = "remotes";

    printf("Before: %s\n",str);
    reverse(str);
    printf("After: %s\n",str);

    system("Pause");
    return 0;
}

void reverse(char s[])
{
    static int i = 0, n;
    char c = s[i];

    if (c != '\0') {
        ++i;
        reverse(s);
        s[n-i] = c;
        --i;
    } 
    else {
        n = i;
    }
}

通常,当递归是代码的最后一步时,递归函数没有问题,因为您应用递归直到某些终止条件和某种向后级联。但是当递归调用之前和之后有代码时,它会让事情变得更加混乱。

4

4 回答 4

5

您在纸上的分析是正确的 - 诀窍是n并且i是静态的(每个堆栈帧都引用变量的同一个实例),而c在堆栈上分配(因此每个堆栈帧都有自己的副本)。

将一些值代入您的分析中,并使用cc'c''c'''来表示 的不同堆栈帧实例c

// original function call: stack frame 0
c = str[0]
i = 1

 // stack frame 1
 c' = str[1]
 i = 2

  // stack frame 2
  c'' = str[2]
  i = 3

   // stack frame 3
   c''' = str[3]
   i = 4

    // stack frame 4
    n = 4

   // unwinding: stack frame 3
   str[0] = c'''
   i = 3

  // unwinding: stack frame 2
  str[1] = c''
  i = 2

 // unwinding: stack frame 1
 str[2] = c'
 i = 1

// unwinding: stack frame 0 (original function call)
str[3] = c
i = 0

编辑:解决您的评论:不要放弃理解递归!一旦你掌握了它并且可以“递归思考”,就很容易编写让你看起来比实际更聪明的代码(一项非常有用的技能!)。例如,对reverse()函数进行小的重构可以创建一个更小的版本:

void reverse(char s[])
{
    static int i = 0;
    char c = s[i++];
    if (c) {
        reverse(s);
        s[i++] = c;
    }
    i = c ? i : (int)c;
}

所以真的,原作者通过比必要的更冗长来帮助你:-)

于 2013-06-14T21:57:31.773 回答
2

您必须考虑 i 和 n 是静态变量,这意味着该函数将在调用之间保留值。

以下是这些值的输出:

i: 1/ n: 0
  i: 2/ n: 0
    i: 3/ n: 0
      i: 4/ n: 0
        i: 5/ n: 0
          i: 6/ n: 0
            i: 7/ n: 0

当 c 变量提示 NULL 值时,函数会更新 n 值并开始反转字符串:

            i: 7/ n: 7/ s: semotes
          i: 6/ n: 7/ s: semotes
        i: 5/ n: 7/ s: setotes
      i: 4/ n: 7/ s: setotes
    i: 3/ n: 7/ s: setomes
  i: 2/ n: 7/ s: setomes
i: 1/ n: 7/ s: setomer
于 2013-06-14T22:06:47.370 回答
1

在这里,我是静态 int 不断增加递归调用,直到 s[i] == '\0'。s[i] 的值也保存在 call frames 的局部变量 'c' 中

当 n == i 时,会发生相反的情况,并且当 i 递减时,局部变量 'c' 的值会被写回。

基本上,跨调用帧的变量“c”的副本会临时保存这些值。

于 2013-06-14T22:08:23.553 回答
0

为了解释这些步骤,我指出了三个断点 A、B 和 C。下表显示了每个阶段的变量。

void reverse(char s[])
{
    static int i = 0, n;
    char c = s[i];
/* A */
    if (c != '\0') {
        ++i;
/* B */
        reverse(s);
/* C */
        s[n-i] = c;
        --i;
/* D */
    } 
    else {
        n = i;
/* E */
    }
}

在此处输入图像描述

于 2013-06-14T23:16:47.123 回答