List implementation in C I tried to implement a Python-esque list in C. Having not really used C in anger, I'd like some pointers on style and error handling in particular.

Header

#ifndef __TYPE_LIST_H__
#define __TYPE_LIST_H__

/*  Generic list implementation for holding a set of pointers to a type
    (has to be consistently handled by the element_match and element_delete 
    functions)
*/

typedef struct list_s list_t;

#include &lt;stdbool.h&gt;
#include &lt;stdint.h&gt;

extern const uint16_t default_capacity;

list_t* list_create(
    uint16_t    initial_capacity,
    bool        (*element_match )(const void* a, const void* b),
    void        (*element_delete)(void* element));

void list_delete(list_t* list);

bool    list_append(list_t* list, void* element);
void*   list_pop(list_t* list);
bool    list_remove(list_t* list, void* element);
int16_t list_index(list_t* list, void* element);
bool    list_contains(list_t* list, void* element);
bool    list_empty(list_t* list);

#endif


Source

#include &lt;type/list.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

const uint16_t default_capacity = 256;

struct list_s
{
    uint16_t length;
    uint16_t capacity;
    void**   elements;
    bool     (*element_match )(const void* a, const void* b);
    void     (*element_delete)(void* element);
};

list_t* list_create(
    uint16_t    initial_capacity,
    bool        (*element_match )(const void* a, const void* b),
    void        (*element_delete)(void* element)) 
{
    list_t* list = (list_t*) malloc(sizeof(list_t));
    if (!list) return NULL;

    if (!initial_capacity) {
        initial_capacity = default_capacity;
    }

    list-&gt;elements = (void**) malloc(sizeof(void*) * initial_capacity);
    if (!list-&gt;elements) return NULL;

    list-&gt;length         = 0;
    list-&gt;capacity       = initial_capacity;
    list-&gt;element_match  = element_match;
    list-&gt;element_delete = element_delete;

    return list;
}

void list_delete(list_t* list) 
{
    if (!list) return;

    if (list-&gt;element_delete) {
        unsigned i;
        for (i = 0; i&lt; list-&gt;length; i++) {
            list-&gt;element_delete(list-&gt;elements[i]);
        }
    }
    else {
        fprintf(stderr, "WARNING: no element_delete specified");
    }
    free(list);
}

bool list_append(list_t* list, void* element) 
{
    if (!list || !element)
        return false;
    if (list-&gt;length &gt;= list-&gt;capacity) {
        // expand the elements array
        list-&gt;capacity *= 2;
        list-&gt;elements = realloc(list-&gt;elements, sizeof(void*) * list-&gt;capacity);
        if (!list-&gt;elements) {
            return false;
        }
    }
    list-&gt;length += 1;
    list-&gt;elements[list-&gt;length] = element;
    return true;
}

void* list_pop(list_t* list)
{
    if (!list || list_empty(list)) {
        return NULL;
    }
    void* element = list-&gt;elements[list-&gt;length];
    list-&gt;elements[list-&gt;length] = NULL;
    list-&gt;length -= 1;
    return element;
}

bool list_remove(list_t* list, void* element)
{
    if (!list || !list-&gt;element_match) {
        return false;
    }
    unsigned i;
    bool found = false;
    for (i = 0; i &lt; list-&gt;length; i++) {
        if (!found &amp;&amp; list-&gt;element_match(list-&gt;elements[i], element)) {
            found = true;
            list-&gt;length -= 1;
        }
        if (found) {
            // shift all subsequent elements back one
            list-&gt;elements[i] = list-&gt;elements[i + 1];
        }
    }
    return found;
}

int16_t list_index(list_t* list, void* element)
{
    int16_t i;
    for (i = 0; i &lt; list-&gt;length; i++) {
        if (list-&gt;element_match(list-&gt;elements[i], element)) {
            return i;
        }
    }
    return -1;
}

bool list_contains(list_t* list, void* element) {
    return (list_index(list, element) != -1);
}

bool list_empty(list_t* list)
{
    return (list-&gt;length == 0);
}
Surprising name

default_capacity is in the public .h file

In a header for list_this and list_that, finding default_capacity is not good and a name-space collision headache.  Suggest list_default_capacity instead.

0 allocation

Be careful about allocating 0 memory (possible with default_capacity == 0 and list_create(0, ...)).  Receiving NULL in that case does not certainly indicate an out-of-memory condition.

// list-&gt;elements = (void**) malloc(sizeof(void*) * initial_capacity);
// if (!list-&gt;elements) return NULL;

list-&gt;elements = malloc(sizeof list-&gt;elements[0] * initial_capacity);
if (list-&gt;elements == NULL &amp;&amp; initial_capacity == 0) {
  return NULL;
}


default_capacity not needed

With generic list code, I'd expect the ability to create a 0 length list and not have 0 indicate "use a default value".