In an earlier entry, we developed a generic way of creating and looping through arrays in C. Arrays are very useful, but for some things linked lists are better. So lets develop some general tools for them.
Decisions
First, a design decision must be made. Each element in the list needs a pointer to the next element, and optionally a pointer to the previous one. Where should these pointers reside? There are a couple of options. The most obvious approach is to add next and previous pointers to whatever structure is used to store your data. This leads to a structure like the figure.

Data Structures for Linked Lists. Direct Approach.
We have a generic container (in green). It stores the number of elements in the list, along with a pointer to the first and last elements. It is up to the user to create the structure that holds the data for each element of the list. And this structure must include the prev and next pointers. This approach is conceptually simple and provides direct access to the data. However, it lacks generality. If you want to make a list containing elements based on a structure from another library, you are out of luck, unless they contain the prev and next pointers. Also, what if you want to store these data items in multiple lists? Can’t be done with a single set of next and prev pointers.
A way around this problem is to add a level of indirection to the scheme. The figure below shows the idea. We now have two predefined structures, one for the list (green) and a new one for a generic list element (blue).

Data Structures for Linked Lists. Indirect Approach.
Now a list can store items of any data type. It could even contain different structures. This generality comes at the price of more complex data access. To retrieve an item from the list, you must do something like:
mylist.pFirst->ListEl->Data1
(green) (blue) (pink)
Instead of
mylist.pFirst->Data1
(green) (pink)
This is one extra level of indirection, compared to the first scheme. It makes the code less readable, is more error prone and is probably slower as well.
Since we are going for generality, we will use the indirect approach, and see how it works.
Another design decision is should the list be linear or circular. In other words, should the next pointer in the last element be set to NULL or to the first element? We’ll keep it simple and create a linear list
Implementation
Here are what our two generic structures look like:
//Generic list Element struct lelement { LElement *next; LElement *prev; void *data; }; //Generic List Structure struct llist { int NumEl; //Number of elements in list LElement *pFirst; //Ptr to first element in list LElement *pLast; //Ptr to last element in list };
The structure for a generic element in the list contains three pointers: next and prev needed for the list, and data which points to the data we are storing. The container structure holds the number of elements in the list, as well as pointers to the first and last elements.
To create an empty list, we just initialize the items in the container:
void CreateList(LList *list) { list->NumEl = 0; list->pFirst = NULL; list->pLast = NULL; }
We assume the memory was allocated elsewhere. An element can be added to the end of the list by adjusting the pointers at the end of the list and in the new element. The code below shows an implementation of this.
LElement *AddToList(void *item, LList *list) { //check inputs assert(item!=NULL); assert(list!=NULL); //Create generic element to hold item ptr LElement *NewEl; NewEl = (LElement *)malloc(sizeof(NewEl)); //create generic element assert(NewEl != NULL); list->NumEl = list->NumEl + 1; NewEl->data = item; if (list->NumEl == 1) { list->pFirst = NewEl; NewEl->prev = NULL; NewEl->next = NULL; } else { NewEl->prev = list->pLast; NewEl->next = NULL; list->pLast->next = NewEl; } list->pLast = NewEl; return NewEl; }
It is also to be able to add data at the beginning or middle of the list. This can be done in a similar way, although there are a few more details to worry about. To remove an item from the list you simply bypass it, then free the memory. Because we don’t know if the data is stored in multiple lists, it is never freed, only the generic list element.
Looping
To loop through a linked list, we can create a macro similar to the one for general arrays:
#define FOR_EACH(item_ptr, list) \ for (ListEl = myList.pFirst; ListEl != NULL; ListEl=ListEl->next)
This macro creates a for loop that initializes ListEl at the first value. It then increments this value at the end of each pass through the loop until the end of the loop is reached. This makes it handy for looping through an already existing list. It is not very helpful in looping through a list under construction.
Summing up
The list code can be found here: ListType.c. It can be compiled with the command
gcc -Wall ListType.c
In creating this example, I hit many snags. It is very easy to make a mistake when rearranging the pointers for an insertion. Then the test code would try to write to a NULL pointer and crash! In comparison, the array list was much easier to develop. If I was going to use linked lists for general storage like this, I would want a well tested library, rather than just winging it.