简体   繁体   中英

Is there any c compiler that shows me each step of a recursive function?

Currently I am writing a quicksort algorithm in c (I'm a beginner) and it's really hard for me to understand the recursion. It is not my first program with a recursive function so I would like to know if there is any compiler for c that shows the steps of a recursive function to get it easier.

Use Visual Studio IDE , There is a free version available. you can view the call stack using this IDE. or Use CodeBlocks IDE

While you can use an IDE to step through the recursion, I find the most instructive thing for students is to use printf at the start of your recursive function. (Actually THE most instructive task is to hand execute a recursive function using paper and pen, but this gets old fast for deep recursion!)

Example:

int fibonacci(int n) {
    printf("fibonacci(%d)\n", n);
    if (n == 0 || n == 1)
    return n;
  else
    return (fibonacci(n-1) + fibonacci(n-2));
}

This will produce the following recursion trace. With double recursion, this unfortunately does not make it clear what calls what:

fib 4
fibonacci(4)
fibonacci(3)
fibonacci(2)
fibonacci(1)
fibonacci(0)
fibonacci(1)
fibonacci(2)
fibonacci(1)
fibonacci(0)
fibonacci(4)=3

If you don't mind adding a recursion count to your recursive function, you can get a nicely indented output using the follow code (as an example):

#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));
}

This accepts a command line number and computes the fibonacci and display the recursion. For example, if you compiled this into executable fib, then the following command:

fib 5

Produces the following output that shows the recursion.

> 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

Do something similar for your QuickSort to create a similar recursion trace.

As an extension to the excellent suggestion by ScottK , I recommend using Graphviz to aid in the visualization. It is available for all operating systems, and is completely free. It takes human-readable text (Dot language) as input, and provides rather nice graphs as output.

Consider the following Fibonacci example program:

#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;
}

It takes a single parameter on the command line, and outputs the recursive call graph for the simple recursive Fibonacci implementation.

For example, if we compile and run the above dot-fibonacci.c in Linux using GCC,

gcc -Wall -O2 dot-fibonacci.c -o dot-fibonacci
./dot-fibonacci 4 | dot -Tx11

we see the call graph 第4次斐波那契数字调用图 The number in front identifies the call to the recursive function ( dot_fibonacci() ), with the outermost fibonacci() call being first. Because my fibonacci() is just a wrapper function that does no calculation, the root node is always the same as the second node (first actual call to dot_fibonacci() ).

The Dot language text that generates the above graph is

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> > ];
}

Note that the order of the indented lines is not important. You can reorder them if you like, for easier understanding.

  • The quoted parts are node identifiers.

  • -> creates an arrow from one node to another.

  • The label=< ... > formats the text inside the label as HTML. You can also use label="string" for simple strings, and shape="record", label="thing | { foo | bar } | baz" for structured labels. You can direct arrows to such subfields as well.

As you can see, the basics are really simple; dot (or one of the other visualizers in the Graphviz package) really does the hard part of choosing how to represent the data.

To implement such dot graph output in your own programs:

  1. Make sure all informational output goes to standard error; use

      fprintf(stderr, "Information ...\\n"); 

    instead of printf() . Only use printf(...) or fprintf(stdout, ...) when outputting the Dot language stuff. This lets you redirect the standard output from your program to a file by appending > filename.dot to the command line.

  2. Start your directional graph with

      printf("digraph {\\n"); 

    and end it with

      printf("}\\n"); 

    The edges and nodes go in between. Their order does not matter.

    (If you want an non-directional graph, use graph { , and -- for the edges.)

  3. A node is defined using "id" [ ... ]; where id is the identifier used to address that node, and ... is a comma-separated list of attributes.

    Typical attributes you need are shape="record", label="foo | bar" for structured labels; label=< HTML > or shape="none", label=< HTML > for HTML-formatted labels, and label="foo" for simple text labels. If you omit the label (or the entire node specification), the id is used for the label instead.

    To visualize eg trees or lists or anything using pointers, use

      printf(" \\"%p\\" [ ... ];\\n", (void *)ptr); 

    which uses the actual pointer value as the source for the node ID.

  4. Directed edges have form

     "source-id" -> "target-id"; 

    or

     "source-id" -> "target-id" [ color="#rrggbb" ]; 

    In addition to color, you can label the arrows using taillabel , headlabel , and so on.

That's about all you need, although you can find additional examples and documentation all over the web.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM