Create a database in C

Hey, I have this basic server thing going on and I can't seem to allocate
memory correctly and I can't access the memory after I allocated it. I
was hoping people might suggest methods of doing this. I was thinking of
using the socket file descriptor to reference the users.

What did you try, and in what way did it not work?

You already kind of helped me with this problem but I ended up deleting all
of that because I couldn't access access each element. Everything would
compile but it wouldn't work the way I had expected.

The compiler just tells you whether your code is syntactically correct, it doesn't tell you whether it works. It doesn't care whether your program cuts off its own foot as you end it in a semicolon.

Your problem in the other thread wasn't allocating memory. There's nothing wrong with the way you called malloc(). The problem was what you did with the memory after. You kept free()ing things you still needed, using the wrong pointers, accidentally overwriting things, etc. Your algorithm made no sense, and I couldn't correct it because you never explained what you were actually trying to do except in a generic "it uses memory" kind of way.

What you do with memory depends on how you want to arrange it and what you're putting in it.

Well I want to tell you my first problem I have trying to use initialize_descr
below. The function send causes my server to crash. I initialized the data
and all should be well but I can't send data to init->newfd

int initialize_descr(int newfd, struct descr *p)
{
    struct descr *d;
    d = descr_list;
    if ( d != NULL ) 
    {
        while ( d->next != NULL)
        {
            d = d->next;
        }
        d = d->next;
    }    
 
    if(!(d = malloc( sizeof(struct descr))))
    {
        nonfatal("malloc failure");
        return 0;
    }
    d->newfd = newfd;
    d->state = 1;
    d->next = NULL;
    p = d;
}
 

    struct descr *init = NULL;
    
    FD_SET(newfd, &master); /* add to master set */
    if (newfd > fdmax)      /* keep track of the max */
                   fdmax = newfd;
    const char name_prompt[] = "Character name or (n)ew: ";
    initialize_descr(newfd, init);
    send(init->newfd, name_prompt, sizeof name_prompt, 0);

---------- Post updated at 12:36 PM ---------- Previous update was at 12:23 PM ----------

Because of that problem I am semi diverted from using that function. I believe
someone helped me fix that once and it required using a pointer to a pointer or
something that I didn't understand well enough to want to use it.

:wall: That function uses all kinds of things you don't define anywhere and you still aren't telling me what it's supposed to do.

I can see lots of obvious errors in it though -- first and foremost you never actually allocate memory for 'init', so you're just feeding a null into your initialize_descr, you copy around pointers as if that changes their contents(it doesn't) and you attempt to read from NULL with init->newfd.

Your initialize_descr function seems strange too:

int initialize_descr(int newfd, struct descr *p)
{
    struct descr *d;
    d = descr_list;
    if ( d != NULL ) 
    {
        // Okay, you're trying to find the end of the list.
        while ( d->next != NULL)
        {
            d = d->next;
        }

        // Since the while loop ended, we know d->next == NULL.
        // So you're setting d=NULL, making the whole while loop pointless.
        d = d->next;
    }    
 

    // except it was pointless anyway, because the first thing you do
    // with 'd' once you've found the end of the list is trash it,
    // making it point to whatever malloc returns instead.
    if(!(d = malloc( sizeof(struct descr))))
    {
        nonfatal("malloc failure");
        return 0;
    }
    d->newfd = newfd;
    d->state = 1;
    d->next = NULL;

    // This does not do what you think it does.  You are setting the local variable p
    // to the address stored in d.  This does absolutely nothing to the memory
    // at that address.  It also does absolutely nothing to the variable that was
    // fed into this function.
    p = d;
    // No default return value, so you have no idea what it actually returns here
}

I think you're trying to append to the end of the list, right? In which case:

struct descr *initialize_descr(int newfd)
{
    struct descr *d=descr_list;

    if(d == NULL)
    {
      d=malloc(sizeof(struct descr));       // Empty list, so allocate new space
      descr_list=d; // list is no longer empty
    }
    else
    {    // Find the end of the list
        while(d->next != NULL) d=d->next;
        // Allocate new memory and add it to ->next, NOT d
        d->next=malloc(sizeof(struct descr));
        if(d->next == NULL) return(NULL); // alloc error
        // It is now in the list.
        // Set 'd' to the new memory so we can set it below.
        d=d->next;
    }

    if(d == NULL) return(NULL); // alloc error

    // Either way the first 'if' goes, 'd' is now memory we want to initialize.
    d->newfd = newfd;
    d->state = 1;
    d->next = NULL; // It's the end of the list
    return(d); // return a pointer to the new memory
}

...

struct descr *d=initialize_descr(fd);
...

I want to save characters in a game. I want to use a structure so that I can make
mobile units that will smile at you if your room number is the same as theres when
the game loop iterates through a mobile function. I want to be able to save prompts
for the user so they can have cool looking experience mojo and I want to be able to
save each character every five minutes or so but I need to successfully allocate and
access and save and write to functions because you can't use one variable
simultaniously, Otherwise I will have to use files for some of this stuff and I don't
want to have to do that untill the game saves the character. Are you still lost as to
what I am trying to accomplish?

lastly I want to be able to send messages to anyone with the name
errigour or lovesly if tom types tell lovesly <message>.

---------- Post updated at 12:55 PM ---------- Previous update was at 12:55 PM ----------

I'm working on my own mud base.

I edited in more to what I was saying, please reread, hope it's helpful.

I think I have gotten that part down thanks.
How I would un append that list.

---------- Post updated at 01:30 PM ---------- Previous update was at 01:22 PM ----------

I mean will the code below work if descr_list is a global variable?

int readjust_descr()
{
    struct descr *l;
    struct descr *r;
    for(l = descr_list; l; l = l->next)
    {
        if (l->state == 0)
        {
         r = l;
            l = l->next;
            free(r);
        }
    }
    
}

---------- Post updated at 01:32 PM ---------- Previous update was at 01:30 PM ----------

I'm iffy on wether that will work or not because I thought pointers work differently.
Meaning I am worried that if r = l; and then l = l->next, then r will be pointing to
l->next and I will be freeing l->next when I free r.

---------- Post updated at 01:34 PM ---------- Previous update was at 01:32 PM ----------

also thanks again you've been very helpful. I have more questions to ask but I wont
ask untill I get to them.

Forget everything you know about pointers. They have no abilities to change themselves.

int array[32];
int addr1=16;
int addr2=10;

array[addr1]=5;  // array[16]=5
array[addr2]=10;// array[10]=10
addr2=addr1; //addr2=16
// print position 16
printf("[addr2]=%d\n", array[addr2]);

addr2=addr1 does nothing at all to the contents of the array -- would you agree?

int initialize_descr(int newfd, int newval)
{
        newval=newfd;
}

int fd=1, newval=10;
initialize_descr(fd, newval);
printf("%d\n", newval);

This will print 10, would you agree?

Pointers are just index numbers for the huge array of memory that belongs to your process. Copying them and moving them around does absolutely nothing to the contents.

Which means, this code is equivalent:

int array[32];
int *ptr1=array+16; // Points to position 16 in the array
int *ptr2=array+10; // Points to position 10 in the array

(*ptr1)=5; // Set array[16]=5
(*ptr2)=10;//Set array[10]=10
ptr2=ptr1; // ptr2 now points to position 16
// Should print the value of position 16
printf("*ptr2 = %d\n", *ptr2);
int readjust_descr()
{
    struct descr *l, *r;

    for(l = descr_list; l; l = l->next)
    {
        if (l->state == 0)
        {
            r = l;
            l = l->next;
            free(r);
        }
    }    
}

You're getting much closer, but what happens to the descr pointing to the node you freed? It's just a number -- it doesn't change by itself just because you made a library call.

So it's still sitting there pointing to the memory you just freed. That will crash or do weird things next time you read it.

Which means you have to stop one early, so you can free the next node and tell the current node where the list ends.

void readjust_descr()
{
    struct descr *l, *r;

    // What if the very first node needs deleting?
    while(descr_list->state == 0)
    {
        // This is easy:  nothing but descr_list points to it.
        // Save the one after it...
        r=descr_list->next;
        free(descr_list); // free the current...
        descr_list=r; // and make descr_list point to the next.

        // We've deleted the first node, and set descr_list to the next.
        // If the current descr_list is also 0, we'll need to delete it too.
        // That's why this is a while(), not an if().

        // Except, if r was NULL, descr_list->state will now CRASH.  Check first!
        if(descr_list == NULL) return; // empty list, nothing left to do
    }

    while(l->next != NULL) // Loop until we're one from the end
    {
        // Check what comes next.
        while(l->next->state == 0)
        {
                r=l->next->next; // Remember what comes after
                free(l->next); // l->next now points to memory that doesn't exist!
                l->next=r; // Make it point to the rest of the list instead.

                // Now, how do we know the l->next we have now isn't ALSO
                // zero?  l=l->next will skip it!  We have to check again.
                // This is why this is a 'while' instead of an 'if'.

                //...but if next is NULL, next->state will CRASH.
                // So we have to check that too!
                if(l->next == NULL) break; // End of the list!  Stop!
        }

        l=l->next; // Move forward in the list
    }
}

Thanks.

---------- Post updated at 03:52 AM ---------- Previous update was at 03:48 AM ----------

Does this loop through the entire descr_list?

while(descr_list->state == 0)

And why did you do this part?

while(l->next != NULL)

I feel like I'm missing something. Like cause I can't see the reason for looping
through l->next.

---------- Post updated at 03:53 AM ---------- Previous update was at 03:52 AM ----------

I'm just wondering if you did that second part just in case I want l to be iterated
through but I gotta make sure.

---------- Post updated at 05:57 AM ---------- Previous update was at 03:53 AM ----------

Ok help me with this part and I'll prolly understand the readjust_descr
function better. Ok the variables of the function here are the socket
that I will send messages to and a structure defined right before this
function is used. descr_list is a global structure descr structure posted
above the function in the code below. Ok the function works. It makes
the structure defined before it, equal to a Null part of a copy of descr_list.

My problem is the fact that I want descr_list to have struct descr *p as
a part of itself and I thought initializing a null part of d would do that but
now I am thinking that it wont.

Ok so heres a huntch I have at making the structure descr *d part of
descr_list. Maybe I just change the value adescr_list untill it is pointing to
a NULL structure and then make it equal to d. I thought maybe I'll do that at the end of initialize_descr. I'm thinking though about the descr_list
structure and if it will still point to the beginning of the list if I access it in a different function.

struct descr
{
 short int newfd;
 short int state; /* Maximum states allowed, 65536 */
 struct queue *q;
 struct descr *next;
};
 
int initialize_descr(int newfd, struct descr **p)
{
 struct descr *d;
 d = descr_list;
 if( d != NULL)
 {
  while(d->next != NULL)
   d = d->next;
  if(!(d->next = malloc(sizeof(struct descr))))
  {
   nonfatal("malloc failure initializing descr");
   return 0;
  }
  d = d->next;
 }
 else if(!(d = malloc(sizeof(struct descr))))
 {
  nonfatal("malloc failure initializing descr");
  return 0;
 }
 d->newfd = newfd;
 d->state = 1;
 if(!(d->q = malloc(sizeof(struct descr))))
 {
  nonfatal("malloc failure initializing descr queue");
  d->state = 0;
  return 0;
 }
 d->q->next = NULL;
 d->next = NULL;
*p = d;
}

What do I have to add to this function to make a list of player structures out of the global variable descr_list?
something like while descr_list->next != NULL descr_list= descr_list->next descr_list ->next = p. I am thinking that
that might not wrk because descr_list will now point to the structure before p and not at the very first structure
of itself.

---------- Post updated at 06:08 AM ---------- Previous update was at 05:57 AM ----------

I just don't understand.

---------- Post updated at 06:20 AM ---------- Previous update was at 06:08 AM ----------

I think I figured out a way to do this. I'll just add another structure to every
structure created by that thing that points to the first part of itself. I'm gonna post
the finished function when I'm done with it. Maybe you could look at it.

struct descr
{
 short int newfd;
 short int state; /* Maximum states allowed, 65536 */
 struct queue *q;
 struct descr *next;
struct descr *self;
};

---------- Post updated at 06:56 AM ---------- Previous update was at 06:20 AM ----------

So what do you think?
Will she work?
I think I have finally done it.

int initialize_descr(int newfd, struct descr **p)
{
struct descr *d;
if(!(d = malloc(sizeof(struct descr))))
{
nonfatal("malloc failure initializing descr");
return 0;
}
d->newfd = newfd;
d->state = 1;
if(!(d->q = malloc(sizeof(struct descr))))
{
nonfatal("malloc failure initializing descr queue");
d->state = 0;
return 0;
}
d->q->next = NULL;
d->next = NULL;
*p = d;
 
if(descr_list == NULL)
{
descr_list = d;
descr_list->list = descr_list;
}
else
{
while(descr_list->next != NULL)
{
 descr_list=descr_list->next;
}
descr_list->next = d;
descr_list->next->list = descr_list->list;
descr_list = descr_list->list;
}
return 1;
}
 
 

---------- Post updated at 07:28 AM ---------- Previous update was at 06:56 AM ----------

Also will this work?

int readjust_descr()
{
 struct descr *r, *track;
 track = descr_list;
    while(descr_list != NULL)
    {
     if(descr_list->state = 0)
     {
         r=descr_list;
         free(descr_list);
     }
     descr_list = descr_list->next;
    }
    descr_list = track;
}

I explained absolutely everything in my code comments and suggest you read them.

If you delete the current node, the previous node is left pointing to invalid memory.

Address 1:  state=1, next=2
Address 2: state=0, next=NULL

when you call free() on memory block 2, what happens to memory block 1? Absolutely nothing. It still points to memory block 2. It doesn't know it's the last node in the list! Next time you use the list it'll try to access block 2 anyway, and either crash or do strange things.

Whenever you're not at the first node in the list, you need to check and free the next node, not the current one, otherwise you'll leave dangling pointers all over the place.

I explained exactly why I did absolutely everything in the comments I put in my code, I suggest you read them. If you don't understand my comments, ask questions about them, please don't just ignore them.

If I get your meaning, it won't.

A pointer is a memory address and nothing more. Trying to initialize a NULL means trying to write to address 0 in memory, which crashes.

Since you never actually posted your data structures, I certainly can't show you how to fix them.

Changing the pointers doesn't change the memory. Please read the examples I posted for you about array indexes.

// descr **p is pointless, returning a pointer is much less likely
// to be screwed up.
int initialize_descr(int newfd)
{
 struct descr *d=descr_list;

 // why are there absolutely no comments in your code?
 if( d != NULL)
 {
  while(d->next != NULL)
   d = d->next;

  if(!(d->next = malloc(sizeof(struct descr))))
  {
   nonfatal("malloc failure initializing descr");
   return 0;
  }

  d = d->next;
 }
 // We only allocate this memory when the list is empty.
 // But you forgot to put it into descr_list.
 // So the list will always stay empty!
 else if(!(d = malloc(sizeof(struct descr))))
 {
  nonfatal("malloc failure initializing descr");
  return 0;
 }
 d->newfd = newfd;
 d->state = 1;
 if(!(d->q = malloc(sizeof(struct descr))))
 {
  nonfatal("malloc failure initializing descr queue");
  d->state = 0;
  return 0;
 }
 d->q->next = NULL;
 d->next = NULL;
*p = d;
}

How about:

struct descr *initialize_descr(int newfd, struct descr **p)
{
 struct descr *d=descr_list;

 if(d == NULL)
 {
    // The base of the list itself must be updated.
    descr_list=malloc(sizeof(struct descr));
    // then set our own pointer to it too.
    d=descr_list;
    // if malloc failed, return error.
    if(d == NULL) return(NULL);
 }
 else
 {
    // find the end of the list.
    while(d->next != NULL) d=d->next;

    d->next=malloc(sizeof(struct descr));
    // if malloc failed, return error
    if(d->next == NULL) return(NULL);
    // set d to the memory we just allocated.
    d=d->next;
 }

 d->newfd = newfd;
 d->state = 1;

 if(!(d->q = malloc(sizeof(struct descr))))
 {
  nonfatal("malloc failure initializing descr queue");
  d->state = 0;
  return 0;
 }
 d->q->next = NULL;
 d->next = NULL;
 return(d);
}

There's only two times you'd change the value of descr_list.

1) When the list is empty. descr_list must be set to the new first node.
2) When the first node is being deleted. descr_list must be set to the next node.

struct descr
{
 short int newfd;
 short int state; /* Maximum states allowed, 65536 */
 struct queue *q;
 struct descr *next;
struct descr *self;
};

If you need to add a 'self' pointer to keep your code together, you must be doing something very strange indeed.

---------- Post updated at 06:56 AM ---------- Previous update was at 06:20 AM ----------

So what do you think?
Will she work?
I think I have finally done it.
[/quote]
For yog's sake. If you've posted 5 times while I'm asleep, posting a 6th won't make me wake up.

I can't tell if your code works or not since you changed the structures again and didn't post what it now is, but it looks like you've added 3 or 4 useless pointers which are only helpful for working around bugs in your code.

int readjust_descr()
{
 struct descr *r, *track;
 track = descr_list;
    while(descr_list != NULL)
    {
     if(descr_list->state = 0)
     {
         r=descr_list;
         free(descr_list);
     }
     descr_list = descr_list->next;
    }
    descr_list = track;
}

[/quote]
It will not work, for all the reasons I gave you in my last post, which I really, really, really wish you'd read.

---------- Post updated at 11:43 AM ---------- Previous update was at 11:09 AM ----------

And you're still forgetting all your return 1's! The return 0's and error checking are useless if you don't return success when you actually have success!

Here's working code for a linked list.

#include <stdio.h>
#include <stdlib.h>

struct descr
{
    int state;
    struct descr *next;
};

// We pass a pointer to the head pointer for the list since we may need to
// change it.
struct descr *append_list(struct descr **head, int state) // Add new thing to the end
{
    struct descr *list;
    // Create new node
    struct descr *n=malloc(sizeof(struct descr));
    if(n == NULL) return(NULL); // Return error if malloc failed.

    n->state=state;
    n->next=NULL;

    // Empty list?  Set head and we're done.
    if((*head) == NULL)
    {
        (*head)=n;
        return(n); // return success
    }

    list=(*head); // set list to the first node.

    // We know the list must not be empty now.
    // Fast-forward until we're at the last descr.
    while(list->next != NULL) list=list->next;

    // Set the 'next' of the last descr to the new descr.
    list->next=n;

    // 'head' is fine where it is, so we're all done.
    return(n);
}

// Deletes all nodes set to the number 'state'.
int delete_state(struct descr **head, int state)
{
    struct descr *list;
    if((*head) == NULL) return(1); // Nothing to do when empty, but that's okay

    // If the list goes 0-> 0-> 0-> 0, we'll need to delete
    // the first node over and over.  After we delete, the second
    // becomes the first, so we start over.
    while((*head)->state == state)
    {
        struct descr *tmp=(*head)->next;
        free(*head); // Free the first node in the list
        (*head)=tmp; // Set the head pointer to the new first node

        if((*head) == NULL) return(1); // If list==NULL here, the list is empty and we're done!
    }

    // Below here, we're no longer changing the first node, so don't have to
    // worry about updating (*head).
    list=(*head);

    while(list->next != NULL)
    {
        // Keep deleting the node after the current one until we find a node
        // we don't need to delete.  When we delete the node after,
        // the node after that becomes the new 'next', so it's a loop.
        while(list->next->state == state)
        {
            struct descr *tmp=list->next->next; // Save the node after
            free(list->next);
            list->next=tmp;
            // if list->next is NULL, list->next->state will CRASH!
            // So when it is, quit the loop immediately!
            if(list->next == NULL) break;
        }

        list=list->next;
        if(list == NULL) break; // NULL means we hit the end of the list and must stop NOW!
    }

    return(0);
}

void free_list(struct descr **head)
{
    while((*head) != NULL)
    {
        struct descr *tmp=(*head)->next;
        free(*head);
        (*head)=tmp;
    }
}

int main(void)
{
    struct descr *head=NULL; // This is the head pointer our functions change with '(*head)'
    struct descr *n, *a, *b, *c, *d, *e, *f; // These will point to nodes we make.

    // create a list 0->1->2->0->3->0
    a=append_list(&head, 0);
    b=append_list(&head, 1);
    c=append_list(&head, 2);
    d=append_list(&head, 0);
    e=append_list(&head, 3);
    f=append_list(&head, 0);

    e->state=4; // Update the '3' node to '4'

    // print each node.
    for(n=head; n!=NULL; n=n->next)
      printf("-> %d", n->state);
    printf("\n");

    // delete all nodes where state==0
    delete_state(&head, 0);

    // print it again.
    for(n=head; n!=NULL; n=n->next)
      printf("-> %d", n->state);
    printf("\n");

    // Beware that pointers a, d, and f are invalid now since
    // their memory was freed
    // Don't use f->state or anything now.

    // Free the entire list.  It will set head to NULL, too.
    free_list(&head);
}

I didn't spam so you would answer faster I just kept updating my post so
you would see that stuff before you replied. Stuff just hit me while I tried
to make this stuff work.

Anyways I have been using all the stuff you talked about. Could you just
look at my last modification of initialize descriptor and see if it will cause
errors or not.

I know that struct descr **p changes to what I want it to be
but I just wanted to know what you thought about making a linked
list of all the descriptors. I'm worried about the lines under the text
// I think I'm updating my descr_list after this line */

/* this is defined globally */
struct descr *descr_list = NULL;
/* These are defined before main */
struct descr
{
 int newfd;             /* Player socket                             */
 char char_name[11];    /* Character's name                          */
 char char_pass[25];    /* Character's password                      */
 short int sex;         /* Character's sex                           */
 short int state;       /* Maximum states allowed, 65536             */
 short int create_state /* Character creation state                  */
 struct queue *q;       /* Here to queue character input             */
 struct descr *next     /* Used to create a linked list              */
 struct descr *list;    /* Used to define the top of the linked list */
};
 
int initialize_descr(int newfd, struct descr **p)
{
 struct descr *d;
/* ok creating descriptor for struct descr **p */
 if(!(d = malloc(sizeof(struct descr))))
 {
  nonfatal("malloc failure initializing descr");
  return 0;
 }
 d->newfd = newfd;
 d->state = 1;
 if(!(d->q = malloc(sizeof(struct descr))))
 {
  nonfatal("malloc failure initializing descr queue");
  d->state = 0;
  return 0;
 }
 d->q->next = NULL;
 d->next = NULL;
/* *p = d works if I use a pointer to a descr structure
   initialize_descr(newfd, &init)
   init being
   struct descr *init; */
 *p = d;
 
// I think I'm updating my descr_list after this line */
 if(descr_list == NULL)
 {
  descr_list = d;
  descr_list->list = descr_list;
 }
 else
 {
  while(descr_list->next != NULL)
  {
   descr_list=descr_list->next;
  }
  descr_list->next = d;
  descr_list->next->list = descr_list->list;
  descr_list = descr_list->list;
 }
 return 1;
}
 
/* ok these are defined before I use the function initialize_desc 
they are before my main server loop that loops infinetly */
 descr_list->next = NULL; 
 descr_list->list = descr_list;
/* then I use the function just to make sure everything works the way I
   want it to */
struct descr *init;
/* newfd is just an open socket */
 initialize_descr(newfd, &init);

You should not keep the head pointer inside the list, no. You'll still need another head pointer to point to the head pointer -- how else will your program find it? Which makes it a bit redundant.

Try my working code and get back to me.

In case you missed it it was:

#include <stdio.h>
#include <stdlib.h>

struct descr
{
    int state;
    struct descr *next;
};

// We pass a pointer to the head pointer for the list since we may need to
// change it.
struct descr *append_list(struct descr **head, int state) // Add new thing to the end
{
    struct descr *list;
    // Create new node
    struct descr *n=malloc(sizeof(struct descr));
    if(n == NULL) return(NULL); // Return error if malloc failed.

    n->state=state;
    n->next=NULL;

    // Empty list?  Set head and we're done.
    if((*head) == NULL)
    {
        (*head)=n;
        return(n); // return success
    }

    list=(*head); // set list to the first node.

    // We know the list must not be empty now.
    // Fast-forward until we're at the last descr.
    while(list->next != NULL) list=list->next;

    // Set the 'next' of the last descr to the new descr.
    list->next=n;

    // 'head' is fine where it is, so we're all done.
    return(n);
}

// Deletes all nodes set to the number 'state'.
int delete_state(struct descr **head, int state)
{
    struct descr *list;
    if((*head) == NULL) return(1); // Nothing to do when empty, but that's okay

    // If the list goes 0-> 0-> 0-> 0, we'll need to delete
    // the first node over and over.  After we delete, the second
    // becomes the first, so we start over.
    while((*head)->state == state)
    {
        struct descr *tmp=(*head)->next;
        free(*head); // Free the first node in the list
        (*head)=tmp; // Set the head pointer to the new first node

        if((*head) == NULL) return(1); // If list==NULL here, the list is empty and we're done!
    }

    // Below here, we're no longer changing the first node, so don't have to
    // worry about updating (*head).
    list=(*head);

    while(list->next != NULL)
    {
        // Keep deleting the node after the current one until we find a node
        // we don't need to delete.  When we delete the node after,
        // the node after that becomes the new 'next', so it's a loop.
        while(list->next->state == state)
        {
            struct descr *tmp=list->next->next; // Save the node after
            free(list->next);
            list->next=tmp;
            // if list->next is NULL, list->next->state will CRASH!
            // So when it is, quit the loop immediately!
            if(list->next == NULL) break;
        }

        list=list->next;
        if(list == NULL) break; // NULL means we hit the end of the list and must stop NOW!
    }

    return(0);
}

void free_list(struct descr **head)
{
    while((*head) != NULL)
    {
        struct descr *tmp=(*head)->next;
        free(*head);
        (*head)=tmp;
    }
}

int main(void)
{
    struct descr *head=NULL; // This is the head pointer our functions change with '(*head)'
    struct descr *n, *a, *b, *c, *d, *e, *f; // These will point to nodes we make.

    // create a list 0->1->2->0->3->0
    a=append_list(&head, 0);
    b=append_list(&head, 1);
    c=append_list(&head, 2);
    d=append_list(&head, 0);
    e=append_list(&head, 3);
    f=append_list(&head, 0);

    e->state=4; // Update the '3' node to '4'

    // print each node.
    for(n=head; n!=NULL; n=n->next)
      printf("-> %d", n->state);
    printf("\n");

    // delete all nodes where state==0
    delete_state(&head, 0);

    // print it again.
    for(n=head; n!=NULL; n=n->next)
      printf("-> %d", n->state);
    printf("\n");

    // Beware that pointers a, d, and f are invalid now since
    // their memory was freed
    // Don't use f->state or anything now.

    // Free the entire list.  It will set head to NULL, too.
    free_list(&head);
}

Well thank you for your help.