effup, consider this trivial example:
Code:
#include <stdlib.h>
#include <stdio.h>
typedef struct node node;
struct node {
struct node *next;
int value;
};
void prepend_incorrect(node *list, node *item)
{
item->next = list;
list = item;
}
void prepend(node **listptr, node *item)
{
item->next = *listptr;
(*listptr) = item;
}
In C, pointers are passed by value. This means that any changes made to the variables list, listptr, and item in the above two functions are only visible within the functions themselves; they are not propagated to the caller.
In the prepend_incorrect function, the node pointed to by item will have its next field modified to point to list , but the caller will not see any changes to list.
To propagate the change to the caller, we need to get the pointer to the pointer to the first element. Any changes we might do to listptr will still be only visible within the caller, but that's okay: the first element in the list is *listptr, and if we modify it, the caller will see it too.
Here is an example main to show how to use the above prepend function. This one takes one or more integers as command line parameters, prepends each to mylist, then prints out the structure of the mylist in DOT format:
Code:
int main(int argc, char *argv[])
{
node *mylist = NULL;
node *curr;
int value, arg;
char dummy;
if (argc < 2) {
fprintf(stderr, "\nUsage: %s NUMBER [ NUMBER ... ]\n\n", argv[0]);
return EXIT_FAILURE;
}
for (arg = 1; arg < argc; arg++) {
if (sscanf(argv[arg], " %d %c", &value, &dummy) == 1) {
curr = malloc(sizeof *curr);
if (curr == NULL) {
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
curr->next = NULL;
curr->value = value;
prepend(&mylist, curr);
} else {
fprintf(stderr, "%s: Not an integer.\n", argv[arg]);
return EXIT_FAILURE;
}
}
printf("digraph {\n");
printf("\trankdir=\"LR\"; \n");
for (curr = mylist; curr != NULL; curr = curr->next) {
printf("\t\"%p\" [ label=\"%d\" ];\n", curr, curr->value);
printf("\t\"%p\" -> \"%p\";\n", curr, curr->next);
}
printf("\t\"%p\" [ label=\"NULL\", shape=none ];\n", NULL);
printf("}\n");
return EXIT_SUCCESS;
}
First, you'll need Graphviz. It's free, and available for all major platforms (Linux, Windows, Mac). If using Linux, use your package manager to install it; it should be available in the standard software sources for your distribution, no need to go to that website at all (other than read the documentation, maybe).
In Linux, you can compile and run the above (combined into say example.c) using
Code:
gcc -Wall -O2 example.c -o example
./example 1 2 3 4 5 | dot -Tx11
In Windows, compile the example to example.exe, then run
Code:
example 1 2 3 4 5 | dot -Tpng > example.png
and open the generated graph, example.png, in a browser. (I like the svg format much better, but not being a Windows user, I don't know if it is yet supported well enough in Windows.)
It'll look like this:
Of course this example is trivial, each number being prepended to the list one at a time, producing the original list in reverse order.
The DOT language output looks like this:
Code:
digraph {
rankdir="LR";
"0x60e090" [ label="5" ];
"0x60e090" -> "0x60e070";
"0x60e070" [ label="4" ];
"0x60e070" -> "0x60e050";
"0x60e050" [ label="3" ];
"0x60e050" -> "0x60e030";
"0x60e030" [ label="2" ];
"0x60e030" -> "0x60e010";
"0x60e010" [ label="1" ];
"0x60e010" -> "(nil)";
"(nil)" [ label="NULL", shape=none ];
}
so it's a very easy graph description language.
My sneaky reason for posting this message, you see, was to also show how to debug and visualize any pointer-based data structures.
I can't even count how many issues I've solved by printing the DOT format description for a structure before and after some problematic operation (single function call). It's so much easier to exactly see what has happened that way -- and so much easier to discover the bug, too.
Of course, if you typedef your pointer types, all of the above gets very hairy to read, and quite difficult to get right.. which is why I, too, never typedef my pointer types.
For more complicated data structures, Graphviz has other visualizers you can try in addition to dot: neato, twopi, circo, and fdp.