Linked list implementation I wrote some code for linked lists and would like to know what things could/should be done differently.

I'm using an index to keep track of the most important information and have 2 sets of functions, one to use the list just to point to data and the other to store the data in the list.

Here's the code so far:

#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

//Basic structure
typedef struct nNode Node;
struct nNode {
    void *content;
    Node *next;
    Node *previous;
};

//An index will make everything simpler
typedef struct {
    Node *first;
    Node *last;
    size_t node_count;
} List;

//Initialize list index with default values
void init_list(List *root)
{
    root-&gt;first = NULL;
    root-&gt;last = NULL;
    root-&gt;node_count = 0;
}

//Link new element and return pointer to it if successful or NULL
Node *add_node(List *root, void *content)
{
    Node *temp = malloc(sizeof(Node));
    if(temp == NULL){
        return NULL;
    }

    //Fill node    
    temp-&gt;content = content;
    temp-&gt;next = NULL;
    temp-&gt;previous = root-&gt;last; //NULL if first node

    //Handle first element insertion
    if(root-&gt;first == NULL){
        root-&gt;first = temp;
    }

    //Or update previous element
    else {
        root-&gt;last-&gt;next = temp;
    }

    //Update index
    root-&gt;last = temp;            
    ++root-&gt;node_count;

    return temp;    
}

//Unlink element, always successful
void delete_node(List *root, Node *node)
{
    //If it's the first, update index reference
    if(node-&gt;previous == NULL){
        root-&gt;first = node-&gt;next;
    }

    //Or it's safe to update previous node
    else {
        node-&gt;previous-&gt;next = node-&gt;next;
    }    

    //If it's the last, update index reference
    if(node-&gt;next == NULL){
        root-&gt;last = node-&gt;previous;
    }

    //Not the last? Safe to update next node
    else {
        node-&gt;next-&gt;previous = node-&gt;previous;
    }

    //Update count and free memory
    free(node);
    --root-&gt;node_count;
}

//Iterate through all nodes and call a function on every iteration
void iterate(const List *root, void(*function)(Node*))
{
    Node *next; //store in case the node changes
    for(Node *ite = root-&gt;first; ite != NULL; ite = next){
        next = ite-&gt;next;
        (*function)(ite);
    }
}

//Iterate through all nodes backwards and call a function on every iteration
void iterate_backwards(const List *root, void(*function)(Node*))
{
    Node *previous; //store in case the node changes
    for(Node *ite = root-&gt;last; ite != NULL; ite = previous){
        previous = ite-&gt;previous;
        (*function)(ite);
    }
}

//Delete all elements in the list
void delete_list(List *root)
{
    iterate(root, (void(*)(Node*))free);
    init_list(root);
}

////////////////
////// The following functions also store the content in the list
///// instead of just pointing to it. They should be used together
//// in order to avoid memory leaks.
/////////////

//Add node and store a copy of content
Node *add_node_store(List *root, const void *content, size_t content_size)
{
    //Allocate space
    void *copy = malloc(content_size);
    if(copy == NULL){
        return NULL;
    }

    //Copy content
    memcpy(copy, content, content_size);

    //Check if node insertion succeeded
    void *response = add_node(root, copy);
    if(response == NULL){
        free(copy);
    }

    return response;
}

//Unlink node and free stored content
void delete_node_store(List *root, Node *node)
{
    free(node-&gt;content);
    delete_node(root, node);
}

//Free node and its content
static void free_node_store(Node *node)
{
    free(node-&gt;content);
    free(node);
}

//Delete all nodes on the list, free them and their contents
void delete_list_store(List *root)
{
    iterate(root, free_node_store);
    init_list(root);
}
You use root as parameter name when you pass the list around but I think a better name would just be list. root implies that you pass the first node of the list around rather than the container.
++root-&gt;node_count is correct but root-&gt;node_count += 1 reads nicer (imho).
I would consider changing the interface slightly:


Have a List* new_list() method which creates the list (and initializes the object).
delete_list should also delete the list object (probably needs signature change to delete_list(List **list))
Add a method clear_list which just clears the contents of the list.

Right now the programmer has to remember whether a node was added via add_node or add_node_store in order to call the proper delete function. However this could be solved by having a flag in nNode which indicates whether the node owns the content or not. Delete could then automatically do the right thing.