我无法理解这种反向功能是如何工作的。我已经尝试在纸上逐步解决代码所做的事情,但这对我来说没有意义。我对代码所做的最好的(虽然粗略的)解释是:
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;
}
}
通常,当递归是代码的最后一步时,我没有使用递归函数的问题,因为你应用递归直到一些终止条件和向后级联。但是当递归调用之前和之后有代码时,会让事情变得更加混乱。
答案 0 :(得分:5)
您在纸上的分析是正确的 - 诀窍是n
和i
是静态的(每个堆栈帧指的是变量的相同实例),而c
分配在堆栈(因此每个堆栈帧都有自己的副本)。
将一些值替换为分析,并使用c
,c'
,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;
}
所以真的,原作者通过比必要更冗长来帮助你: - )
答案 1 :(得分: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
答案 2 :(得分:1)
这里我是静态int继续使用递归调用递增,直到s [i] =='\ 0'。另外的s [i]值保存在调用帧中的局部变量'c'中。
当n == i时,反向发生, 当我减少时,局部变量'c'的值被写回 。
基本上,调用帧中变量“c”的副本会暂时保存这些值。
答案 3 :(得分: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 */
}
}