Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C generic linked-list

I have a generic linked-list that holds data of type void* I am trying to populate my list with type struct employee, eventually I would like to destruct the object struct employee as well.

Consider this generic linked-list header file (i have tested it with type char*):

struct accListNode                 //the nodes of a linked-list for any data type
{
  void *data;                     //generic pointer to any data type
  struct accListNode *next;       //the next node in the list
};

struct accList                    //a linked-list consisting of accListNodes
{
  struct accListNode *head;
  struct accListNode *tail;
  int size;
};

void accList_allocate(struct accList *theList);           //allocate the accList and set to NULL
void appendToEnd(void *data, struct accList *theList);    //append data to the end of the accList
void removeData(void *data, struct accList *theList);         //removes data from accList
  --------------------------------------------------------------------------------------

Consider the employee structure

struct employee 
{ 
   char name[20]; 
   float wageRate; 
} 

Now consider this sample testcase that will be called from main():

    void test2()
    {
      struct accList secondList;
      struct employee *emp = Malloc(sizeof(struct employee));
      emp->name = "Dan";
      emp->wageRate =.5;

      struct employee *emp2 = Malloc(sizeof(struct employee));
      emp2->name = "Stan";
      emp2->wageRate = .3;

      accList_allocate(&secondList);
      appendToEnd(emp, &secondList);
      appendToEnd(emp2, &secondList);

      printf("Employee: %s\n", ((struct employee*)secondList.head->data)->name);   //cast to type struct employee
      printf("Employee2: %s\n", ((struct employee*)secondList.tail->data)->name);  
    }

Why does the answer that I posted below solve my problem? I believe it has something to do with pointers and memory allocation. The function Malloc() that i use is a custom malloc that checks for NULL being returned.

Here is a link to my entire generic linked list implementation: https://codereview.stackexchange.com/questions/13007/c-linked-list-implementation

like image 238
CodeKingPlusPlus Avatar asked Dec 05 '25 20:12

CodeKingPlusPlus


2 Answers

The problem is this accList_allocate() and your use of it.

struct accList secondList;
accList_allocate(&secondList);

In the original test2() secondList is memory on the stack. &secondList is a pointer to that memory. When you call accList_allocate() a copy of the pointer is passed in pointing at the stack memory. Malloc() then returns a chunk of memory and assigns it to the copy of the pointer, not the original secondList.

Coming back out, secondList is still pointing at uninitialised memory on the stack so the call to appendToEnd() fails.

The same happens with the answer except secondList just happens to be free of junk. Possibly by chance, possibly by design of the compiler. Either way it is not something you should rely on.

Either:

struct accList *secondList = NULL;

accList_allocate(&secondList);

And change accList_allocate()

accList_allocate(struct accList **theList) {
    *theList = Malloc(sizeof(struct accList));
    (*theList)->head = NULL;
    (*theList)->tail = NULL;
    (*theList)->size = 0;
}

OR

struct accList secondList;

accList_initialise(secondList);

With accList_allocate() changed to accList_initialise() because it does not allocate

accList_initialise(struct accList *theList) {
    theList->head = NULL;
    theList->tail = NULL;
    theList->size = 0;
}
like image 185
SpacedMonkey Avatar answered Dec 08 '25 10:12

SpacedMonkey


I think that your problem is this:

  1. You've allocated secondList on the stack in your original test2 function.
  2. The stack memory is probably dirty, so secondList requires initialization
  3. Your accList_allocate function takes a pointer to the list, but then overwrites it with the Malloc call. This means that the pointer you passed in is never initialized.
  4. When test2 tries to run, it hits a bad pointer (because the memory isn't initialized).

The reason that it works when you allocate it in main is that your C compiler probably zeros the stack when the program starts. When main allocates a variable on the stack, that allocation is persistent (until the program ends), so secondList is actually, and accidentally, properly initialized when you allocate it in main.

Your current accList_allocate doesn't actually initialize the pointer that's been passed in, and the rest of your code will never see the pointer that it allocates with Malloc. To solve your problem, I would create a new function: accList_initialize whose only job is to initialize the list:

void accList_initialize(struct accList* theList)
{
    // NO malloc
   theList->head = NULL;
   theList->tail = NULL;
   theList->size = 0;
}

Use this, instead of accList_allocate in your original test2 function. If you really want to allocate the list on the heap, then you should do so (and not mix it with a struct allocated on the stack). Have accList_allocate return a pointer to the allocated structure:

struct accList* accList_allocate(void)
{
   struct accList* theList = Malloc( sizeof(struct accList) );
   accList_initialize(theList);
   return theList;
}
like image 34
sfstewman Avatar answered Dec 08 '25 12:12

sfstewman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!