[英]My gcc compiler gives me an, error that shows as Conflicting types for (function)
[英]Is there any c compiler that shows me each step of a recursive function?
目前,我正在用c(我是初学者)编写快速排序算法,这对我来说很难理解递归。 这不是我的第一个具有递归函数的程序,所以我想知道是否有用于c的编译器显示递归函数的步骤,以使其更容易使用。
使用Visual Studio IDE,有一个免费版本。 您可以使用此IDE查看调用堆栈。 或使用CodeBlocks 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
如果您不介意在递归函数中添加递归计数,则可以使用以下代码获得缩进的输出(作为示例):
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
void traceResursion(const char *funcName, int n, int recursionLevel)
{
int i;
if (recursionLevel > 0) {
for(i=0; i<recursionLevel; i++)
printf(" ");
printf("-->");
}
printf("%s(%d)\n", funcName, n);
}
int fibonacci(int n, int recursionLevel) {
traceResursion("fibonacci", n, recursionLevel);
if (n == 0 || n == 1)
return n;
else
return (fibonacci(n-1, recursionLevel+1) + fibonacci(n-2, recursionLevel+1));
}
int main(int argc, char **argv){
int n = 2;
/* Get n from the command line if provided */
if (argc > 1)
n = atoi(argv[1]);
printf("fibonacci(%d)=%d\n", n, fibonacci(n,0));
}
这将接受命令行编号并计算斐波那契并显示递归。 例如,如果将其编译为可执行文件fib,则使用以下命令:
fib 5
产生以下显示递归的输出。
> 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
对您的QuickSort执行类似的操作以创建类似的递归跟踪。
作为ScottK的出色建议的扩展 ,我建议使用Graphviz进行可视化。 它适用于所有操作系统,并且完全免费。 它以人类可读的文本(点语言)作为输入,并提供相当不错的图形作为输出。
考虑下面的斐波那契示例程序:
#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;
}
它在命令行上使用单个参数,并输出用于简单递归Fibonacci实现的递归调用图。
例如,如果我们使用GCC在Linux中编译并运行上述dot-fibonacci.c ,
gcc -Wall -O2 dot-fibonacci.c -o dot-fibonacci
./dot-fibonacci 4 | dot -Tx11
我们看到了调用图 前面的数字标识对递归函数的调用( dot_fibonacci()
),最外面的fibonacci()
调用位于第一个。 因为我的fibonacci()
只是一个不执行任何计算的包装函数,所以根节点始终与第二个节点相同(第一次实际调用dot_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> > ];
}
注意,缩进线的顺序并不重要。 您可以根据需要重新排序,以方便理解。
引用的部分是节点标识符。
->
创建从一个节点到另一个节点的箭头。
label=< ... >
将标签内的文本格式化为HTML。 您还可以将label="string"
用于简单的字符串,并将shape="record", label="thing | { foo | bar } | baz"
用于结构化标签。 您也可以将箭头指向此类子字段。
如您所见,基础非常简单。 dot
(或Graphviz程序包中的其他可视化工具之一)确实在选择如何表示数据方面很困难。
要在自己的程序中实现这样的点图输出:
确保所有信息输出均达到标准错误; 采用
fprintf(stderr, "Information ...\\n");
而不是printf()
。 输出点语言内容时fprintf(stdout, ...)
仅使用printf(...)
或fprintf(stdout, ...)
。 这样,您可以通过在命令行后添加> filename.dot
将标准输出从程序重定向到文件。
用以下命令启动方向图
printf("digraph {\\n");
并以
printf("}\\n");
边缘和节点位于两者之间。 他们的顺序无关紧要。
(如果要使用无方向图,请使用graph {
,和--
表示边缘。)
使用"id" [ ... ];
定义节点"id" [ ... ];
其中id
是用于寻址该节点的标识符,而...
是逗号分隔的属性列表。
您需要的典型属性是用于结构化标签的shape="record", label="foo | bar"
; label=< HTML >
或shape="none", label=< HTML >
用于HTML格式的标签, label="foo"
用于简单的文本标签。 如果省略标签(或整个节点规范),则将id
用作标签。
要可视化例如树或列表或使用指针的任何内容,请使用
printf(" \\"%p\\" [ ... ];\\n", (void *)ptr);
它使用实际指针值作为节点ID的来源。
有向边有形式
"source-id" -> "target-id";
要么
"source-id" -> "target-id" [ color="#rrggbb" ];
除了颜色外,还可以使用taillabel
, headlabel
标记箭头。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.