GIDForums  

Go Back   GIDForums > Computer Programming Forums > C Programming Language
User Name
Password
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
 
Thread Tools Search this Thread Rate Thread
  #1  
Old 04-Jul-2007, 17:26
Howard_L Howard_L is offline
Regular Member
 
Join Date: Apr 2007
Location: Maryland/PA, USA
Posts: 582
Howard_L has a spectacular aura aboutHoward_L has a spectacular aura about

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):
CPP / C++ / C Code:
/*  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)
CPP / C++ / C Code:
/*      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
  #2  
Old 04-Jul-2007, 20:35
seprich seprich is offline
Member
 
Join Date: Jun 2007
Posts: 110
seprich has a spectacular aura aboutseprich has a spectacular aura about

Re: Linked List demo using a single pointer


Hi,

Ok. Just few points:
  1. I would like to see user interaction more separated from the linked list operations. Functions like sort_list() would be more clean if you don't have user interaction mixed with linked list operations. But that's just me. There is no rules really.
  2. Quote:
    CPP / C++ / C Code:
    extern int mallocount;
    you don't need to declare it external because 'extern' variable is a variable which resides in another compiling unit. In fact you have declared 'mallocount' in the beginning of the file outside all function bodies and that makes it a global variable. Therefore you just use it from inside functions without having any declarative mention about it. .. I guess compiler won't complain about abuse of "extern" but still...
  3. Because initially your linked list already have 5 members and you don't have (yet) delete_node() function so you don't yet have a problem but ... consider what happen with sort_list() if there is 0 - 1 nodes in the list...
  4. Quote:
    CPP / C++ / C Code:
    /* 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.
    So if comp is the node number I think (if I understood your code) you should not count to fixed number like 10... ten iterations is not enough to bubble sort longer than 10 node lists.
    [edit: removed faulty example]
  5. Ok just for fuN.:: if you like really sophisticated solution you can consider implementing linked list based on something like:
    CPP / C++ / C Code:
    // (sll stands for singly linked list)
    typedef struct sll_node {
        void *data;
        size_t  size;
        struct sll_node *next;
    } sll_node;
    
    typedef struct sl_list {
        sll_node *head;
        long nodecount;
    };
    
    // sample definition for append_node
    // (size needed if we assume that append_data creates independent copy aka mallocs and memcpy:s the data. ):
    int append_node( sl_list *list, void *data, size_t size );
    
    // and definition for sorter:
    int bubble_sort( sl_list *list, int (*comparator)(sll_node *, sll_node *) );
    // notice that the 2nd parameter is function pointer to comparator.
    // so for sorting in ascending order or descending order you just use different
    // comparators
    
    // then in your program you have e.g.
    typedef struct {
        int data1;
        char name[10];
    } myDataPair;
    
    // and to append node
    sl_list mylist;
    myDataPair newOne;
    
    newOne.data1 = 5;
    strncpy( newOne.name, "foo", 9 );
    append_node( &mylist, &newOne, sizeof( myDataPair ) );
    
    
    If separating the void * -based linked list code to separate file then you have the general re-usable code which you can apply to your different programs to hold whatever different kinds of structures you like.
  #3  
Old 04-Jul-2007, 22:23
Howard_L Howard_L is offline
Regular Member
 
Join Date: Apr 2007
Location: Maryland/PA, USA
Posts: 582
Howard_L has a spectacular aura aboutHoward_L has a spectacular aura about

Re: Linked List demo using a single pointer


Thanks for the thoughts , and your ideas on the functions. Pretty fancy stuff! Days-O-Fun there!
Quote:
user interaction more separated from the linked list operations
I realize that but I left it like that as a demo since it was already in place. It gives a good visual if you wanted and if you didn't you could remove all the extra verbage easily enough. After seeing your examples though I now see that it's actually not a very good demo.... good thing you added yours!
Quote:
CPP / C++ / C Code:
extern int mallocount;
...you don't need to declare it external because 'extern' variable is a variable which resides in another compiling unit. In fact you have declared 'mallocount' in the beginning of the file outside all function bodies and that makes it a global variable.
Oh boy, this is news to me. Global is different than external? I thought it was good form to declare data as extern in each function it's used regardless of whether it was declared a separate file or in head of this file, just that it was not declared in main(). No, I get no compiler complaints with or without the 'extern'. hmmm another compiling unit... ok I guess I'll stop worrying about it if it's in the head of the 'compiling unit' it's declared in... like data items in the head of an .h file can be used by functions in the same file without extern, right?

But wait, I thought extern ensured that you were NOT using a local object by the same name.... hmmm You can declare say 'int foo' in head and one within a function and they would be different objects. Oh , your saying with no declaration in the function the 'global' object will be used.... ok, makes sense...
Quote:
..what <will> happen with sort_list() if there is 0 - 1 nodes in the list
Interesting... I'd imagine a segfault, so I should have a safeguard in place? I'll put it on the 'think about' list...
CPP / C++ / C Code:
for(test = 't', comp = 1; comp < 10 ; hptr = hptr->next) {
Ah! There it is... I'm glad it's just that. I had a feeling it was silly, that was a left over 'emergency break' from testing. I was late getting out of here for an engagement and didn't have time to check further. ...works much better now.

And your llist ideas are great. void * , huh, Jensen ends his pointer tutorial with the use of void * for versitile array sorting. I haven't quiet gotten that advanced in practice but I'm starting to get the concept of void * thinking. You have posted some very nice thinking and examples which will help!
Thank You very much, ++Howard;
PS: WOW! we both got a 23:23 timestamp. What are the odds!
  #4  
Old 04-Jul-2007, 22:23
seprich seprich is offline
Member
 
Join Date: Jun 2007
Posts: 110
seprich has a spectacular aura aboutseprich has a spectacular aura about

Re: Linked List demo using a single pointer


and about the sorting without special code for the first element:
ok the sorting was somewhat tricky but now I ended up with following solution:
CPP / C++ / C Code:
// assuming other code like in your example...

int nodecount( struct node **head ) {
    int count=0;
    while( *head != NULL ) {
        count++;
        head = &((*head)->next);
    }
    return count;
}

// the somewhat tricky function to switch two nodes:
// a and b are the addresses of the pointers pointing to nodes under interest
void switch_nodes( struct node **a, struct node **b ) {
    struct node *tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
    // and also have to take care of the next pointers:
    tmp = (*a)->next;
    (*a)->next = (*b)->next;
    (*b)->next = tmp;
}

// now the sort_list() using bubble sort (and mode as parameter):
void sort_list( struct node **head, char mode ) {
    int iter, idx, count;
    struct node **a, **b;

    count = nodecount( head );
    if( count >= 2 ) {
        for( iter=0; iter < count-1; iter++ ) {
            a = head;
            for( idx=0; idx < count-iter-1; idx++ ) {
                b = &((*a)->next);
                if( ( mode == 'a' && ( (*a)->data1 > (*b)->data1 ) ) ||
                    ( mode == 'd' && ( (*a)->data1 < (*b)->data1 ) ) )
                    switch_nodes( a, b );
                a = b;
            }
        }
    }
}
  #5  
Old 04-Jul-2007, 23:19
Howard_L Howard_L is offline
Regular Member
 
Join Date: Apr 2007
Location: Maryland/PA, USA
Posts: 582
Howard_L has a spectacular aura aboutHoward_L has a spectacular aura about

Re: Linked List demo using a single pointer


Wow, you mean you just whip that stuff up like that! amazing
It'll take me hours to undertand, but I keep trying...
I didn't know if you noticed my reply to your first post.
Got it in scant seconds before your second. Going back to reading ,
thanks, Howard;
  #6  
Old 05-Jul-2007, 11:12
Howard_L Howard_L is offline
Regular Member
 
Join Date: Apr 2007
Location: Maryland/PA, USA
Posts: 582
Howard_L has a spectacular aura aboutHoward_L has a spectacular aura about

Re: Linked List demo using a single pointer


After giving it a look for a while, I decided to put seprich's nifty sort_list() function set in place to observe values being passed by all that pointer to pointer stuff. But there is a problem with something which causes segfault. I'm not sure how to go about fixing it yet.
So far I've:
- deleted my old sort_list()
- inserted seprich's three functions
- added the three function declarations in header area:
CPP / C++ / C Code:
     int nodecount( struct node **head );
     void switch_nodes( struct node **a, struct node **b );
     void sort_list( struct node **head, char mode );
- changed the call from menu:
CPP / C++ / C Code:
      case 's': {
        printf(" call to sort_list:  sort_list(head2);   working! \n");
        c = 0;
        while(c != 'q') {
          printf("sort (a)scending or (d)escending (q)uit ");
          while((c = c_choice()) != 'a' || c != 'd' || c != 'q') {
            if(c == 'a' || c == 'd') {
              printf(" going to sort_list() \n");
              sort_list(head2, c);
            }
          }
        }
        break;
      }
- ran and got a segfault........
- added some reporting printfs and think that maybe a wrong pointer address is being used???
CPP / C++ / C Code:
Here's some output:
Begin main() ...declaration: struct node *head = NULL;
&head= 0073FDF4 , head= 00000000 , *head=(%x gets segfault (000000 outabounds)
. . . . .
-----  print_list()  -----
  &head= 0073FDB0 , head= 0073FDF4 , *head= 00860850 , mallocount= 5
loop= 0, tmptr= 00860850, ->data1=   4, ->name=       Jim, ->next= 00860830
loop= 1, tmptr= 00860830, ->data1=   2, ->name=       Tom, ->next= 00860810
loop= 2, tmptr= 00860810, ->data1=   1, ->name=     Chuck, ->next= 008607F0
loop= 3, tmptr= 008607F0, ->data1=   3, ->name=      Mack, ->next= 008607D0
loop= 4, tmptr= 008607D0, ->data1=   5, ->name=       Joe, ->next= 00000000
=====  Menu()  =====
(p)rint_list, (a)dd_node, (s)ort_list, e(x)it, (optionally: (l)oad_nodes ) : s

 call to sort_list:  sort_list(head2);   working!
sort (a)scending or (d)escending (q)uit a
 going to sort_list()
   in sort_list() head= 0073FDF4, *head= 00860850
   in nodecount()  head= 0073FDF4, *head= 00860850
   in switch_nodes a= 0073FDF4, *a= 00860850, b= 00860860, *b= 00860830
    tmp = *a;  done
    *a = *b;   done
    *b = tmp;  done
    (*a)->next = (*b)->next;  done
    (*b)->next = tmp;         done
   back in sort_list() iter= 0, idx= 0, a= 0073FDF4 b= 00860860
   back in sort_list() iter= 0, idx= 1, a= 00860860 b= 00860820
   back in sort_list() iter= 0, idx= 2, a= 00860820 b= 00860800
      ***  SEGFAULT  , END OF OUTPUT  ***

Shouldn't it operate on the value of head (the node addresses)...
...instead of addresses based on the address of head?

At this point, I'm at a loss understanding things like:
b = &((*a)->next); . . . what the heck is that saying???
The address of (the (value of a's) ->next) ??? sheeze....
It will take me a while to understand this even more advanced pointer to pointer usage!
Anyhow here are seprich's functions as I have them now which give the ouput shown above:
CPP / C++ / C Code:
/* assuming other code like in your example... */
int nodecount( struct node **head ) {
    int count=0;
    printf("   in nodecount()  head= %p, *head= %p \n", (void*)head, (void*)*head);
    while( *head != NULL ) {
        count++;
        head = &((*head)->next);
    }
    return count;
}
/* the somewhat tricky function to switch two nodes: */
/* a and b are the addresses of the pointers pointing to nodes under interest */
void switch_nodes( struct node **a, struct node **b ) {
    struct node *tmp;

    printf("   in switch_nodes a= %p, *a= %p, b= %p, *b= %p \n", (void*)a, (void*)*a, (void*)b, (void*)*b );
    tmp = *a;
    printf("    tmp = *a;  done\n");
    *a = *b;
    printf("    *a = *b;   done\n");
    *b = tmp;
    printf("    *b = tmp;  done\n");
    /* and also have to take care of the next pointers: */
    tmp = (*a)->next;
    (*a)->next = (*b)->next;
    printf("    (*a)->next = (*b)->next;  done\n");
    (*b)->next = tmp;
    printf("    (*b)->next = tmp;         done\n");
}
/* now the sort_list() using bubble sort (and mode as parameter): */
void sort_list( struct node **head, char mode ) {
    int iter, idx, count;
    struct node **a, **b;

    printf("   in sort_list() head= %p, *head= %p \n", (void*)head, (void*)*head);
    count = nodecount( head );
    if( count >= 2 ) {
        for( iter=0; iter < count-1; iter++ ) {
            a = head;
            for( idx=0; idx < count-iter-1; idx++ ) {
                b = &((*a)->next);
                if( ( mode == 'a' && ( (*a)->data1 > (*b)->data1 ) ) ||
                    ( mode == 'd' && ( (*a)->data1 < (*b)->data1 ) ) )
                    switch_nodes( a, b );
                printf("   back in sort_list() iter= %d, idx= %d, a= %p b= %p \n",iter, idx, (void*)a, (void*)b );
                a = b;
            }
        }
    }
}
I will continue trying to understand how I can get this to work. Any suggestions?
Howard;
  #7  
Old 05-Jul-2007, 13:42
seprich seprich is offline
Member
 
Join Date: Jun 2007
Posts: 110
seprich has a spectacular aura aboutseprich has a spectacular aura about

Re: Linked List demo using a single pointer


sorry I didn't test the code i posted earlier... now that I tested it
there was small error in that sort_list function.

the line "a = b;" should be replaced with "a = &((*a)->next);"
then it works.

here the corrected version:
CPP / C++ / C Code:
// now the sort_list() using bubble sort (and mode as parameter):
void sort_list( struct node **head, char mode ) {
    int iter, idx, count;
    struct node **a, **b;

    count = nodecount( head );
    if( count >= 2 ) {
        for( iter=0; iter < count-1; iter++ ) {
            a = head;
            for( idx=0; idx < count-iter-1; idx++ ) {
                b = &((*a)->next);
                if( ( mode == 'a' && ( (*a)->data1 > (*b)->data1 ) ) ||
                    ( mode == 'd' && ( (*a)->data1 < (*b)->data1 ) ) )
                    switch_nodes( a, b );
                a = &((*a)->next);
            }
        }
    }
}
  #8  
Old 05-Jul-2007, 14:06
seprich seprich is offline
Member
 
Join Date: Jun 2007
Posts: 110
seprich has a spectacular aura aboutseprich has a spectacular aura about

Re: Linked List demo using a single pointer


explaining of the meaning of "&((*a)->next)" is best to be done with a picture or diagram (well i try some ascii thing here):

Code:
node **a, **b; ok.. here we have the runtime system of the linked list: head --> Node 0 --> Node 1 --> Node 2 |-data1 / |-data1 / |-data1 |-name / |-name / |-name |-next --- |-next --- |-next ---> NULL where assumed that head is "struct node *head;" ( notice next is also type (struct node *) ) Now initially when sorting we set a = &head: head --> Node 0 --> Node 1 --> Node 2 ^ |-data1 / |-data1 / |-data1 ^ |-name / |-name / |-name ^ |-next --- |-next --- |-next ---> NULL ^ a therefore if doing b = &((*a)->next); it means that (*a) is same as pointer to Node 0, and (*a)->next is the next pointer of Node 0 but we take the address of that pointer and so that b will point to the pointer like in following: head --> Node 0 --> Node 1 --> Node 2 ^ |-data1 / |-data1 / |-data1 ^ |-name / |-name / |-name ^ |-next --- |-next --- |-next ---> NULL ^ ^ a b Now the *b points to Node 1 and *a points to Node 0. we compare data1 values and flip the order if necessary .. the flipping means that pointers where a and b point to have to be manipulated.
  #9  
Old 05-Jul-2007, 14:26
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,821
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: Linked List demo using a single pointer


Quote:
Originally Posted by seprich
...somewhat tricky...

Just looking at the "swap" function, it seems to me that you should either swap the entire structs or just change pointers to rearrange the list without touching the data values. I can't see why you would do both. (Or maybe I am missing something???)


Swapping values of any two list elements is easily done; you can just pass pointers to the two elements. Then you copy everything in the struct except the ->next pointer values. This may be very practical if the structs don't contain much data. Here is a trivial example. Note you just pass pointers, not pointers-to-pointers. We are not swapping structs, just moving the data values.
CPP / C++ / C Code:
#include <stdio.h>

typedef struct s_ { int value; struct s_ *next;} s;

void printlist(s*);
void swap_values(s *first, s *second);

int main()
{
    s a, b, c, d, e;
    s *head = &a;

    a.value = 111111;
    a.next = &b;

    b.value = 2222;
    b.next = &c;

    c.value = 333;
    c.next = &d;

    d.value = 44;
    d.next = &e;

    e.value = 5;
    e.next = NULL;
   

    printf("In main, initially               :");
    printlist(head);
    printf("\n");

    swap_values(&c, &d);

    printf("In main(), after swapping c and d:");
    printlist(head);

    return 0;
}

void printlist(s *start)
{
    while (start) {
        printf("%6d ", start->value);
        start = start->next;
    }
    printf("\n");
}


void swap_values(s *first, s *second)
{
    int temp = first->value;
    first->value = second->value;
    second->value = temp;
}

Code:
In main, initially :111111 2222 333 44 5 In main(), after swapping c and d:111111 2222 44 333 5


If the structs have lots and lots of data, you might want to rearrange the list by changing pointer values and leaving the data contents alone. In other words you "break" some existing links and re-make them to reflect the new list arrangement.

For certain types of sorts (bubble-sort, for example), the sorting function will require changing the relative locations of two successive list elements.

Here's one way to look at it.

Suppose we have a linked list with six elements. I visualize the list without regard to data values as:

a->b->c->d->e->f

If these are structs in a c program like the previous examples, then the list was built by making:

a->next = &b
b->next = &c
c->next = &d
d->next = &e
e->next = &f


Now, suppose we want to "swap c and d". That is, we will rearrange the pointers so that the list now looks like the following:

a->b->d->c->e->f

We don't touch data values of the structs, we just change the "next" pointer values of some of the elements.

What did we have to do?

1. New value of b->next is &d
2. New value of d->next is &c
3. New value of c->next is &e.

So, in general, we have to change three pointer values. If we want to make a function that swaps two list entries by changing pointers, we could give it a pointer to the struct just before the first of the swapees. Then everything is done by following the links.

Without trying any kind of optimization, the code could look something like:

CPP / C++ / C Code:
#include <stdio.h>

typedef struct s_ { int value; struct s_ *next;} s;

void printlist(s*);
void swap_the_next_two_using_pointers(s *pointer);

int main()
{
    s a, b, c, d, e;
    s *head = &a;

    a.value = 111111;
    a.next = &b;

    b.value = 2222;
    b.next = &c;

    c.value = 333;
    c.next = &d;

    d.value = 44;
    d.next = &e;

    e.value = 5;
    e.next = NULL;
   

    printf("In main(), initially             :");
    printlist(head);
    printf("\n");

    swap_the_next_two_using_pointers(&b);

    printf("In main(), after swapping c and d:");
    printlist(head);

    return 0;
}

void printlist(s *start)
{
    while (start) {
        printf("%6d ", start->value);
        start = start->next;
    }
    printf("\n");
}



void swap_the_next_two_using_pointers(s *pointer)
{
    /* comments here apply to a case where you have a list
     * like a->b->c->d->e and you want to swap c and d
     * Then call this routine with the value of "pointer"
     * equal to the address of b
     */

    /* Be sure that there really are two successive elements
     * If not, then don't change anything
     */
    if (pointer && (pointer->next) && (pointer->next->next)) {
        s *temp1 = pointer->next;             /* points to c */
        s *temp2 = pointer->next->next;       /* points to d */
        s *temp3 = pointer->next->next->next; /* points to e */

        /* You can suppress printout by commenting out the #define */
#define DEBUG
#ifdef DEBUG
        printf("In swap_the_next_two_using_pointers:\n");
        printf("  pointer                   = %p\n", pointer);
        printf("  pointer->next             = %p\n", pointer->next);
        printf("  pointer->next->next       = %p\n", pointer->next->next);
        printf("  pointer->next->next->next = %p\n\n", pointer->next->next->next);

#endif
        pointer->next             = temp2; /* now b points to d */
        pointer->next->next       = temp1; /* now d points to c */
        pointer->next->next->next = temp3; /* now b points to e */

#ifdef DEBUG
        printf("After swapping:\n");
        printf("  pointer                   = %p\n", pointer);
        printf("  pointer->next             = %p\n", pointer->next);
        printf("  pointer->next->next       = %p\n", pointer->next->next);
        printf("  pointer->next->next->next = %p\n\n", pointer->next->next->next);
#endif
    }
}

Code:
In main(), initially :111111 2222 333 44 5 In swap_the_next_two_using_pointers: pointer = 0x22ccb8 pointer->next = 0x22ccb0 pointer->next->next = 0x22cca8 pointer->next->next->next = 0x22cca0 After swapping: pointer = 0x22ccb8 pointer->next = 0x22cca8 pointer->next->next = 0x22ccb0 pointer->next->next->next = 0x22cca0 In main(), after swapping c and d:111111 2222 44 333 5

Note that to use this in a bubble sort, you might have to make a special case where the two elements are the first two in the list. (I think that's why some people put the dummy at the head of the list). Of course, someone might point out that maybe bubble sort particularly suited to linked lists, but that's not the real topic of interest here. After all, the goal isn't to make the world's best sorting program, but to understand what the heck is going on with linked lists and how to understand how to manipulate them. At least that's how I see it.


Regards,

Dave
  #10  
Old 05-Jul-2007, 15:12
seprich seprich is offline
Member
 
Join Date: Jun 2007
Posts: 110
seprich has a spectacular aura aboutseprich has a spectacular aura about

Re: Linked List demo using a single pointer


Quote:
Originally Posted by davekw7x
Just looking at the "swap" function, it seems to me that you should either swap the entire structs or just change pointers to rearrange the list without touching the data values. I can't see why you would do both. (Or maybe I am missing something???)
What code are you referring to ?
 
 

Recent GIDBlogToyota - 2008 November Promotion by Nihal

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
[Include] Doubly-linked List dsmith C Programming Language 6 14-Apr-2006 14:12
linked list error message Krandygrl00 C++ Forum 4 22-Jun-2005 15:13
[Tutorial] Pointers in C (Part II) Stack Overflow C Programming Language 0 27-Apr-2005 18:36
search linked list itsmecathys C++ Forum 20 18-Apr-2005 02:34

Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The

All times are GMT -6. The time now is 07:33.


vBulletin, Copyright © 2000 - 2008, Jelsoft Enterprises Ltd.