是否有任何c编译器向我显示递归函数的每个步骤?
目前我正在用c语言编写一个快速排序算法(我是初学者),对我来说理解递归非常困难。这不是我的第一个使用递归函数的程序,因此我想知道是否有任何c编译器可以显示递归函数的步骤,以使其更简单。使用Visual Studio IDE,有一个免费版本可用。 您可以使用此IDE查看调用堆栈。是否有任何c编译器向我显示递归函数的每个步骤?,c,recursion,C,Recursion,目前我正在用c语言编写一个快速排序算法(我是初学者),对我来说理解递归非常困难。这不是我的第一个使用递归函数的程序,因此我想知道是否有任何c编译器可以显示递归函数的步骤,以使其更简单。使用Visual Studio IDE,有一个免费版本可用。 您可以使用此IDE查看调用堆栈。 或者使用代码块IDE虽然您可以使用IDE逐步完成递归,但我发现对学生来说最有启发性的事情是在递归函数的开头使用printf。(实际上,最有启发性的任务是使用纸和笔手动执行递归函数,但这对于深度递归来说很快就会过时!) 例
或者使用代码块IDE虽然您可以使用IDE逐步完成递归,但我发现对学生来说最有启发性的事情是在递归函数的开头使用printf。(实际上,最有启发性的任务是使用纸和笔手动执行递归函数,但这对于深度递归来说很快就会过时!) 例如:
int fibonacci(int n) {
printf("fibonacci(%d)\n", n);
if (n == 0 || n == 1)
return n;
else
return (fibonacci(n-1) + fibonacci(n-2));
}
这将产生以下递归跟踪。不幸的是,对于双递归,这并不能清楚地说明什么叫做什么:
fib 4
fibonacci(4)
fibonacci(3)
fibonacci(2)
fibonacci(1)
fibonacci(0)
fibonacci(1)
fibonacci(2)
fibonacci(1)
fibonacci(0)
fibonacci(4)=3
如果不介意将递归计数添加到递归函数中,可以使用以下代码(作为示例)获得缩进良好的输出:
生成以下显示递归的输出
> fib 5
fibonacci(5)
-->fibonacci(4)
-->fibonacci(3)
-->fibonacci(2)
-->fibonacci(1)
-->fibonacci(0)
-->fibonacci(1)
-->fibonacci(2)
-->fibonacci(1)
-->fibonacci(0)
-->fibonacci(3)
-->fibonacci(2)
-->fibonacci(1)
-->fibonacci(0)
-->fibonacci(1)
fibonacci(5)=5
对快速排序执行类似的操作,以创建类似的递归跟踪。作为对优秀建议的扩展,我建议使用来帮助可视化。它适用于所有操作系统,并且完全免费。它以人类可读的文本(点语言)作为输入,并提供相当漂亮的图形作为输出 考虑以下斐波那契示例程序:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static inline int unique_id(void)
{
static int id = 0;
return ++id;
}
static int dot_fibonacci_recursive(int n, int parent_id, FILE *out)
{
const int id = unique_id();
if (n < 2) {
printf(" \"%d\" [ label=< %d. F<sub>%d</sub> = <b>1</b> > ];\n", id, id, n);
printf(" \"%d\" -> \"%d\";\n", id, parent_id);
return 1;
} else {
int result1, result2;
result1 = dot_fibonacci_recursive(n - 1, id, out);
result2 = dot_fibonacci_recursive(n - 2, id, out);
printf(" \"%d\" [ label=< %d. F<sub>%d</sub> = <b>%d</b> > ];\n", id, id, n, result1 + result2);
printf(" \"%d\" -> \"%d\";\n", id, parent_id);
return result1 + result2;
}
}
int fibonacci(int n)
{
const int id = unique_id();
int result;
printf("digraph {\n");
result = dot_fibonacci_recursive(n, id, stdout);
printf(" \"%d\" [ label=< %d. F<sub>%d</sub> = <b>%d</b> > ];\n", id, id, n, result);
printf("}\n");
fflush(stdout);
return result;
}
int main(int argc, char *argv[])
{
int n, fn;
char dummy;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s N > graph.dot\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program will compute the N'th Fibonacci number\n");
fprintf(stderr, "using a recursive function, and output the call graph\n");
fprintf(stderr, "as a dot language directed graph.\n");
fprintf(stderr, "\n");
fprintf(stderr, "Use e.g. 'dot' from the Graphviz package to generate\n");
fprintf(stderr, "an image, or display the graph interactively. For example:\n");
fprintf(stderr, " dot -Tsvg graph.dot > graph.svg\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (sscanf(argv[1], " %d %c", &n, &dummy) != 1 || n < 0) {
fprintf(stderr, "%s: Invalid N.\n", argv[1]);
return EXIT_FAILURE;
}
fn = fibonacci(n);
fprintf(stderr, "%d'th Fibonacci number is %d.\n", n, fn);
return EXIT_SUCCESS;
}
我们看到调用图
前面的数字标识对递归函数的调用(dot\u fibonacci()
),最外面的fibonacci()
调用是第一个调用。因为myfibonacci()
只是一个不进行计算的包装函数,所以根节点始终与第二个节点相同(第一次实际调用dot\u fibonacci()
)
生成上述图形的点语言文本为
digraph {
"5" [ label=< 5. F<sub>1</sub> = <b>1</b> > ];
"5" -> "4";
"6" [ label=< 6. F<sub>0</sub> = <b>1</b> > ];
"6" -> "4";
"4" [ label=< 4. F<sub>2</sub> = <b>2</b> > ];
"4" -> "3";
"7" [ label=< 7. F<sub>1</sub> = <b>1</b> > ];
"7" -> "3";
"3" [ label=< 3. F<sub>3</sub> = <b>3</b> > ];
"3" -> "2";
"9" [ label=< 9. F<sub>1</sub> = <b>1</b> > ];
"9" -> "8";
"10" [ label=< 10. F<sub>0</sub> = <b>1</b> > ];
"10" -> "8";
"8" [ label=< 8. F<sub>2</sub> = <b>2</b> > ];
"8" -> "2";
"2" [ label=< 2. F<sub>4</sub> = <b>5</b> > ];
"2" -> "1";
"1" [ label=< 1. F<sub>4</sub> = <b>5</b> > ];
}
而不是printf()
。仅在输出点语言内容时使用printf(…)
或fprintf(stdout…)
。通过在命令行中添加>filename.dot
,可以将程序的标准输出重定向到文件中。 printf("digraph {\n");
并以
printf("}\n");
边和节点介于两者之间。他们的顺序无关紧要
(如果需要无方向图,请使用graph{
,并使用--
作为边。)“id”[…];
定义的,其中id
是用于寻址该节点的标识符,…
是以逗号分隔的属性列表
对于结构化标签,您需要的典型属性是shape=“record”,label=“foo | bar”
;对于HTML格式标签,需要label=
;对于简单文本标签,需要label=“foo”
。如果省略标签(或整个节点规范),则标签将使用id
要使用指针可视化树、列表或任何内容,请使用
printf(" \"%p\" [ ... ];\n", (void *)ptr);
它使用实际指针值作为节点ID的源。"source-id" -> "target-id";
或
除颜色外,还可以使用taillabel
、headlabel
等标记箭头这就是你所需要的一切,尽管你可以在网上找到更多的工具。不是编译器,而是调试器。有了现代化的IDE,它对用户很友好,你可以一步一步地看到执行情况,直到解决问题为止。记得在要求工具推荐时指定你正在使用的环境。非常感谢!我以前没有这样做过o写在纸上,但是因为我必须在程序运行之前测试好几次,所以我浪费了很多纸。非常感谢!我会尝试的。
printf("digraph {\n");
printf("}\n");
printf(" \"%p\" [ ... ];\n", (void *)ptr);
"source-id" -> "target-id";
"source-id" -> "target-id" [ color="#rrggbb" ];