|
Linked List demo using a single pointer
Hello again and Happy Independence Day,
After the discussion last week I was compelled to try to create a singly linked list which operates by declaring only one single struct pointer in main().
(yes there are a bunch more in the functions)
It has not only been an excercise on linked lists, but on passing pointers, pointers to pointers and working out what I believe is a bubble sort as well. . As I went along I loaded it up with verbosity and printed data values to be able to see what the heck was going on and it has turned out to be fairly good demo.
I am interested in comments you all might have on whatever and testing to see where it breaks... I could be heading down a wrong path in my thinking on some of these concepts that would be better off addressed sooner rather than later. Like posting too much stuff!
I have a problem with the sort at this point though (bottom). I guess I still need to add a delete_node() and I'd like to add a test in the sort to split and work with strings but thought I'd go ahead and post this now before it gets more bloated (or this message):
/* llist_13.c singly linked list
(using (boy are we using) ONLY one(1)(UNO) main() declared pointer ) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/******** external datatype declarations ****************/
struct node { /* some folk use a typedef here in lieu of or with the tag */
int data1;
char name[10];
struct node * next; /* one pointer for use in singly linked list */
};
int mallocount = 0;
int loadnumbers[5] = { 5, 3, 1, 2, 4};
char loadstrings[5][6] = {"Joe", "Mack", "Chuck", "Tom", "Jim"};
int cc;
#define FLUSHIN(cc) while( ( cc = getc(stdin)) != EOF && cc != '\n')
/******** function prototypes ***************************/
int menu(struct node *head ,struct node **head2);
int load_nodes(struct node **head);
int add_node(struct node **head);
int print_list(struct node **head);
int sort_list(struct node **head);
int free_all(struct node **head);
void mygets(char chin[], int size);
int c_choice(void);
#define PAUSE c_choice()
/******** functions ***************************/
int main(void)
{
struct node *head = NULL;
printf("-----\nBegin main() ...declaration: struct node *head = NULL; \n");
printf("&head= %p , head= %p , *head=(%%x gets segfault (000000 outabounds))\n", (void*)&head, (void*)head );
printf("\n\
OK, so there's the pointer head's address and the value it holds (an address)\n\
which at this point is NULL. Now we make the call to menu , and I'll use \n\
two different arguments to see how they are picked up in menu():\n\
menu( head , &head ); ...pass the value and address of head to menu() \n" );
menu( head , &head );
printf("-----\nAnd we're back in main() at the end: \n&head= %p , head= %p \n SAY BYE BYE! \n", (void*)&head, (void*)head );
return 0;
}
/************************************************/
int menu(struct node *head ,struct node **head2)
{
int c = 0;
printf("-----\n\
Now we're in menu where we picked up the two arguments like this: \n\
int menu(struct node *head ,struct node **head2) \n\n\
First note the 'local' address of these arguments: \n\
&head= %p , &head2= %p \n\
(Note that 'local' means 'in the scope of this function' ) \n\n\
Now well looka the the values they now hold: \n\
head= %p , head2= %p , *head2= %p \n", (void*)&head, (void*)&head2, (void*)head, (void*)head2, (void*)*head2 );
printf("\n\
Ah,,, now I can see here that **head2 gets the address of head properly.\n\
So here's the menu() \n");
while(head2) {
printf("===== Menu() ===== \n(p)rint_list, (a)dd_node, (s)ort_list, e(x)it, (optionally: (l)oad_nodes ) : ");
c = c_choice();
putchar(0x0a);
switch(c) {
case 'p': {
printf(" call to print_list: print_list(head2); \n");
print_list(head2);
break;
}
case 'a': {
printf(" call to add_node: \n add_node(head2); (pass original address on to add_ node) \n");
add_node(head2);
break;
}
case 's': {
printf(" call to sort_list: sort_list(head2); working! \n");
sort_list(head2);
break;
}
case 'x': {
printf("-----\n Before we exit menu() ...observe that head now holds these values: \n\
head= %p , head2= %p , *head2= %p \n\
On our way out, we will free() allocated memory using this call: \n\
free_all(head2);\n ALWAYS FREE ALLOCATED MEMORY! (they say) \n", (void*)head, (void*)head2, (void*)*head2 );
PAUSE; /* pause */
free_all(head2);
return 1;
}
case 'l': {
printf(" load_node: loading nodes (for you modes:) with load_nodes(head2);\n");
load_nodes(head2);
break;
}
default: {
printf("Your Input was Invalid, Try Again... \n");
} /* close last case */
} /* close switch */
} /* close while */
return 1;
}
/************************************************/
int load_nodes(struct node **head)
{
struct node *tmptr;
extern int mallocount;
int i;
printf("\n----- load_node() ----- \n\
&head= %p , head= %p , *head= %p \n", (void*)&head, (void*)head, (void*)*head );
for(i = 0; i < 5; i++) {
tmptr = (struct node *)malloc(sizeof(struct node));
tmptr->data1 = loadnumbers[i] ;
strcpy(tmptr->name , loadstrings[i]);
tmptr->next = *head;
*head = tmptr;
mallocount++;
printf("loop= %d, mallocount= %d, head= %p, (data1= %3d, name= %9s) loaded! \n", i, mallocount, (void*)tmptr, tmptr->data1, tmptr->name );
}
return 0;
}
/************************************************/
int add_node(struct node **head)
{
struct node *tmptr;
extern int mallocount;
char nametmp[11];
int d1;
printf("-----\n\
Ok, now we're in add_node() where we pick up the argument like this:\n\
int add_node(struct node **head)\n\n\
Let's take a look at those values:\n\
&head= %p , head= %p , *head= %p \n\n\
Keep in mind that the &head gets the LOCAL address of head IN THIS FUNCTION! \n\
Now we'll allocate a few nodes, swap the addresses, and examine head values:\n", (void*)&head, (void*)head, (void*)*head );
do {
/* I'm gonna separate user input from the load */
printf("\nEnter a number for data1 (0 quits) : ");
scanf("%d", &d1);
while((getc(stdin)) != '\n');
if(d1 == 0)
break;
printf("Enter a short name: ");
mygets(nametmp, sizeof(nametmp));
tmptr = (struct node *)malloc(sizeof(struct node)) ;
/* assign those input values into the new node members */
tmptr->data1 = d1; /* assign from input */
strcpy(tmptr->name , nametmp); /* assign from input */
tmptr->next = *head; /* swap addresses */
*head = tmptr; /* swap addresses */
mallocount++; /* increment the total allocations thusfar */
printf("&head= %p, head= %p, *head= %p, data1= %d, name= %s \n", (void*)&head, (void*)head, (void*)*head, tmptr->data1, tmptr->name );
}
while(head);
printf("\nmallocount= %d , See how it's working! \n\n", mallocount);
return 1;
}
/************************************************/
int print_list(struct node **head)
{
struct node *tmptr;
extern int mallocount;
int i;
printf("\n----- print_list() ----- \n\
&head= %p , head= %p , *head= %p , mallocount= %d \n"
, (void*)&head, (void*)head, (void*)*head, mallocount );
tmptr = *head;
while(tmptr) {
printf("loop= %d, tmptr= %p, ->data1= %3d, ->name= %9s, ->next= %p \n"
, i, (void*)tmptr, tmptr->data1, tmptr->name, (void*)tmptr->next );
if(tmptr->next == NULL )
break;
tmptr = tmptr->next;
i++;
}
return 1;
}
/************************************************/
int sort_list(struct node **head)
{
struct node *tmp, *tmp2, *hptr, *hptr2;
/* tmp is for... you know
hptr is for moving through the list
hptr2 is for interim list head storage instead of using extern *head
I'll assign that address to *head at the end of the sort function */
int comp, loopcount, mode, sorts, test;
tmp = NULL;
hptr = hptr2 = *head; /* initial both to the present head of the list */
comp = loopcount = mode = sorts = test = 0;
printf("\n----- sort_list() ----- \n\
&head= %p , head= %p , *head= %p , mallocount= %d \n"
, (void*)&head, (void*)head, (void*)*head, mallocount );
printf("Choose (a)scending or (d)ecending: ");
mode = c_choice();
/* First loop ----------
starts from head of list returns after run makes it to NULL */
for(sorts = loopcount = 1; sorts != 0 ; loopcount++) {
sorts =0;
/* Second loop ----------
goes into the actual node comparisons, routing and swapping
and it stops upon reaching the last node (the NULL ->next) */
for(test = 't', comp = 1; comp < 10 ; hptr = hptr->next) {
/* 'comp' represents which node we're on.
The first is done differently than the rest as it's at the list head */
if(comp == 1) { /* do the 'head' of the list node */
printf("in comp1 , ");
printf("hptr->data1= %d, hptr->next->data1= %d \n", hptr->data1, hptr->next->data1);
if(mode == 'a' && hptr->data1 > hptr->next->data1) {
test = 'f';
printf("comp%d test '%c' is '%c'\n", comp, mode, test);
}
if(mode == 'd' && hptr->data1 < hptr->next->data1) {
test = 'f';
printf("comp%d test '%c' is '%c'\n", comp, mode, test);
}
if(test == 'f') { /* swap the nodes (head node method) */
printf("test is 'f'\n");
tmp = hptr->next;
tmp2 = hptr->next->next;
hptr->next->next = hptr;
hptr->next = tmp2;
hptr = tmp;
hptr2 = hptr; /* new head address for starting next loop */
sorts++;
/****** I think this is no longer needed because of #2 below *****
if(hptr->next->next == NULL) {
printf(" stopped here for NULL in comp1 \n");
break;
}
*/
}
printf("end of comp1: mode= %c, test= %c \n", mode, test);
}
comp++;
/* now do the other nodes in the list , break at NULL */
printf("in comp%d , hptr->next->data1= %d, hptr->next->next->data1= %d\n", comp, hptr->next->data1, hptr->next->next->data1);
if(mode == 'a' && hptr->next->data1 > hptr->next->next->data1) {
test = 'f';
printf("comp%d test '%c' is '%c'\n", comp, mode, test);
}
if(mode == 'd' && hptr->next->data1 < hptr->next->next->data1) {
test = 'f';
printf("comp%d test '%c' is '%c'\n", comp, mode, test);
}
if(test == 'f') { /* swap the nodes (head node method) */
tmp = hptr->next->next->next;
tmp2 = hptr->next->next;
hptr->next->next->next = hptr->next;
hptr->next->next = tmp;
hptr->next = tmp2;
sorts++;
/****** I think this is no longer needed because of #2 below *****
if(hptr->next->next->next == NULL) {
printf(" stopped here for NULL in comp%d \n", comp);
break;
}
*/
/* hptr = hptr->next we do this in the for loop */
}
printf("end of comp%d: mode= %c, test= %c \n", comp, mode, test);
if(hptr->next->next->next == NULL) {
printf(" stopped here for NULL in comp%d (#2) \n", comp);
break;
}
} /* end comparison loop */
printf(" loopcount == %d , hptr= %p \n", loopcount, (void*)hptr);
hptr = hptr2;
printf(", hptr = hptr2; hptr now= %p \n", (void*)hptr);
if(hptr->next == NULL) {
printf(" stopped at end of loop for NULL (#3) \n");
break;
}
print_list(&hptr2);
printf(" (anykey) ");
PAUSE; /* pause at end of loop 1, see what we gots */
}
*head = hptr2;
return 0;
}
/************************************************/
int free_all(struct node **head)
{
struct node *tmptr;
extern int mallocount;
int c = 1;
printf("\n-----\n In free_all() we pick up head like this:\n\
int free_all(struct node *head) ....which holds these values:\n\
&head= %p , head= %p , *head= %p \n\n\
Here we'll use a 'struct node *tmptr' to swap addresses around. (see code)\n\
And for our grand finale: free_all()ing allocated memory: \n\
----------------------------------------------------------\n", (void*)&head, (void*)head, (void*)*head );
if(mallocount == 0) {
printf("mallocount= %d , No allocations have been made, no free()ing to be done. \n", mallocount);
return 1;
}
tmptr = *head;
while(tmptr) {
printf("loop=%2d, mallocount=%2d , head=%p , data1= %3d , name= %9s is free! \n", c, mallocount--, (void*)tmptr, tmptr->data1, tmptr->name );
free(tmptr);
if(tmptr->next == NULL)
break;
tmptr = tmptr->next;
c++;
}
*head = tmptr->next;
return 1;
}
/************************************************/
void mygets(char chin[], int size)
{
int c, i, j;
i = j = 0;
printf(" (%d chars max) ", size-1 );
do {
c = getchar();
/* if conditions are right store the char */
if( i <= (size - 1) ) {
/* if we have a \n already, loop to fill remainder with \0's */
if(c == '\n')
for( ; i <= size -1; i++)
chin[i] = '\0';
else if(i == size -1) chin[i] = '\0';
else chin[i] = c;
}
/* increment to next array element */
i++;
}
while(c != '\n'); /* keep reading stdin until \n is reached */
}
/************************************************/
int c_choice(void)
{
int c, d;
while((d = getc(stdin)) != '\n')
c = d;
return c;
}
In the sort I found I needed to do a different routine for the first node than on the rest. . I also wound up using two tmp pointers after trying to get it done with fewer. . I wondered if anyone could see annother way that I just don't have the capacity to figure out. . (not good with Rubiks Cube) Here are the notes of my thinking: (beware)
/* OK so I'll try to illustrate a short llist thusly:
the list:
235 127 155 386 <- allocation addresses
3 2 6<---->8 <- value to be sorted (int data1 in this example)
000 235 127 155 <- 'next' addresses
----- Now to work out a bubble sort routine ----------------------------------
. hptr=386 <-(pointed at head of the list )
235 127 155 386
3 2 6<---->8
000 235 127 155 <- ahh! this is 127 after step2 making step3 wrong
or000
tmp=-+ tmp = hptr->next (155)
+--->--+ hptr->next = hptr->next->next (127)
+ =hptr hptr->next->next = hptr (386)
hptr = hptr2 = tmp (155)
THIS ONE IS WRONG (swap hpt2 in loop 1 only)
------------------------------------------------------------------------------
TRY SOME MORE WITH 1 TMP hptr=386 <-(pointed at head of the list )
235 127 155 386 (can't seem to get it right with one tmp)
3 2 6<---->8
000 235 127 155
or000
tmp= + tmp = hptr->next->next (127)
+=hptr hptr->next->next = hptr (386)
+=tmp hptr->next = tmp (127)
hptr = hptr2 = hptr->next (155)
This is WRONG so far...
------------------------------------------------------------------------------
2 TMPS WORKS . hptr=386 <-(pointed at head of the list )
235 127 155 386
3 2 6<---->8
000 235 127 155
or000
tmp= + tmp = hptr->next (155)
tmp2= + tmp2 = hptr->next->next (155)
+ =hptr hptr->next->next = hptr (386)
+=tmp2 hptr->next = tmp2 (127)
hptr = hptr2 = tmp (155)
(hptr2 swap in loop 1 only)
THIS ONE WORKS ... I have failed to find a 1 tmp method.....
------------------------------------------------------------------------------
235 127 155 386 hptr=155
3 2 6<---->8
000 235 386 127 showing values swapped
or000
------------------------------------------------------------------------------
235 127 386 155 hptr=155
3 2 8<---->6
000 235 127 386 illustrating new order placement
or000 ahHa! so we could now check for NULL in
if(hptr->next->next == NULL)
and terminate the loop... ok... otherwise:
ok, so now is 8<6? ...no hptr = hptr->next..... (386)
(this check/move is now changed for loop one, see below)
-------------------------------------------------------------------
now is 2<8? ...yes so now what? will the same routine work
for second node ?
. hptr=386
235 127 386 155 tmp = hptr->next (127)
3 2<---->8 6 hptr->next = hptr->next->next (235)
000 235 127 386 hptr->next->next = hptr (386)
hptr = tmp (127)
-------------------------------------------------------------------
. hptr=127
235 127 386 155
3 2 8 6 answer? NO
000 386 235 386 head of list->next need to be changed to 127...
hmmm, but I can't get to that from where I was standing on list number 2 (386).
So what to do????
- How about on the first loop don't move to next and operate from there.
I would then be able to reach ->next, ->next->next, and ->next->next->next
..hmmm
- Another way would be to declare a dummy node which would precede head and
all the loops would operate the same... just need that reach range!
So let's take the first approach...
- first we don't move hptr to next. let's get the picture back:
------------------------------------------------------------------------------
hptr=155 <-after no move on loop 1
now the check becomes
if(hptr->next->next->data1 < hptr->next->data1) //do blah, so here's some 'blah'
.
235 127 386 155
3 2<---->8 6
000 235 127 386 <- same problem we had above, now trying 2 tmps...
or000 tmp= --+ tmp = hptr->next (386)
+--->--+ aahnt-wrong hptr->next = hptr->next->next (127)
+-->---+ hptr->next->next = hptr->next->next->next (235)
+-- =tmp hptr->next->next->next = tmp (386)
------------------------------------------------------------------------------
.
235 127 386 155 hptr=155 <-after no move on loop 1
3 2<---->8 6
000 235 127 386 <- same problem we had above, trying 2 tmps...
or000
tmp= + tmp = hptr->next->next->next (235)
tmp2= + tmp2 = hptr->next->next (127)
+---<-----<---+ hptr->next->next->next = hptr->next (386)
+ =tmp hptr->next->next = tmp (235)
+ =tmp2 hptr->next = tmp2 (127)
if(hptr->next->next = NULL) break;
and move to next hptr = hptr->next (127)
------------------------------------------------------------------------------
235 127 386 155 -A picture with the swapped values shown
3 2 8 6
000 386 235 127
or000
-------------------------------------------------------------------------------
235 386 127 155 -A picture representing the sort order thusfar
3 8 2 6
000 235 386 127 -ok, Now do the NULL check on hptr->next->next->next
or000 -if ok then move hptr = hptr->next
-Hey I think I'm getting somewhere!
-the hptr->next->next->next one would see the NULL !
-think I can code it? ewwww....
*/
My question about the sort is a problem I just noticed. When I do a few node_loads() and have say 20 nodes, the sort kinda works but it has an upper half and lower half, each in sequence in thier half! wierd. I'll have to do some more looking to see why. Sorry.
Code:
----- print_list() -----
&head= 0073FDB0 , head= 0073FDF4 , *head= 00860870 , mallocount= 20
loop= 0, tmptr= 00860870, ->data1= 5, ->name= Joe, ->next= 00860910
loop= 1, tmptr= 00860910, ->data1= 5, ->name= Joe, ->next= 008609B0
loop= 2, tmptr= 008609B0, ->data1= 5, ->name= Joe, ->next= 00860990
loop= 3, tmptr= 00860990, ->data1= 4, ->name= Jim, ->next= 00860A30
loop= 4, tmptr= 00860A30, ->data1= 4, ->name= Jim, ->next= 00860930
loop= 5, tmptr= 00860930, ->data1= 3, ->name= Mack, ->next= 008609D0
loop= 6, tmptr= 008609D0, ->data1= 3, ->name= Mack, ->next= 00860970
loop= 7, tmptr= 00860970, ->data1= 2, ->name= Tom, ->next= 00860A10
loop= 8, tmptr= 00860A10, ->data1= 2, ->name= Tom, ->next= 008609F0
loop= 9, tmptr= 008609F0, ->data1= 1, ->name= Chuck, ->next= 00860950
loop= 10, tmptr= 00860950, ->data1= 1, ->name= Chuck, ->next= 008607D0
loop= 11, tmptr= 008607D0, ->data1= 5, ->name= Joe, ->next= 008608F0
loop= 12, tmptr= 008608F0, ->data1= 4, ->name= Jim, ->next= 00860850
loop= 13, tmptr= 00860850, ->data1= 4, ->name= Jim, ->next= 00860890
loop= 14, tmptr= 00860890, ->data1= 3, ->name= Mack, ->next= 008607F0
loop= 15, tmptr= 008607F0, ->data1= 3, ->name= Mack, ->next= 00860830
loop= 16, tmptr= 00860830, ->data1= 2, ->name= Tom, ->next= 008608D0
loop= 17, tmptr= 008608D0, ->data1= 2, ->name= Tom, ->next= 008608B0
loop= 18, tmptr= 008608B0, ->data1= 1, ->name= Chuck, ->next= 00860810
loop= 19, tmptr= 00860810, ->data1= 1, ->name= Chuck, ->next= 00000000
So there it is... How's that for some Holiday Fun ? !
++Howard;
Last edited by admin : 04-Jul-2007 at 22:39.
Reason: Used [code] tag for example output
|