Basic cons cells in C So, I'm diving into C and decided to create cons cells. I'd like some thoughts on style and correctness. There are no warnings with -Wall -g and Valgrind doesn't complain about anything.

I'm really not sure what to do when the list is NULL in the first function.

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

struct Cell {
    int val;
    struct Cell *next;
};

struct Cell *cons(int val, struct Cell *rest)
{
    struct Cell *cell = malloc(sizeof(struct Cell));
    cell-&gt;val = val;
    cell-&gt;next = rest;
    return cell;
}

int first(struct Cell *list)
{
    if (list) {
        return list-&gt;val;
    }
    return 0;
}

struct Cell *rest(struct Cell *list)
{
    return list-&gt;next;
}

int length(struct Cell *list)
{
    if (list) {
        return 1 + length(rest(list));
    }
    return 0;
}

void destroy(struct Cell *list)
{
    if (list) {
        destroy(rest(list));
        free(list);
    }
}

void print(struct Cell *list)
{
    if (list) {
        printf("%d\n", first(list));
        print(rest(list));
    }
}


The main function.

int main(int argc, char *argv[])
{
    struct Cell *list = NULL;
    int i;
    for (i = 1; i &lt; argc; ++i) {
        list = cons(atoi(argv[i]), list);
    }
    puts("Entire list:");
    print(list);
    puts("First:");
    printf("%d\n", first(list));
    puts("Rest:");
    print(rest(list));
    puts("Length:");
    printf("%d\n", length(list));
    destroy(list);
    return 0;
}


Running it.

./cons 1 2 3 4
A few comments:

typedef your structs:

typedef struct Cell
{
    int val;
    struct Cell *next;
} Cell;


typedef means you no longer have to write struct all over the place:

Cell *cons(int val, Cell *rest)
{
    Cell *cell = malloc(sizeof(Cell));
    cell-&gt;val = val;
    cell-&gt;next = rest;
    return cell;
}


That not only saves keystrokes, it also can make the code cleaner since it provides a bit more abstraction.



I would check if the pointer is NULL rather than if it isn't (related question):

int length(struct Cell *list)
{
    if (!list) return NULL; // or something else like -1
    return 1 + length(rest(list));
}




Set the pointer to NULL after freeing it.

void destroy(struct Cell *list)
{
    if (list)
    {
        destroy(rest(list));
        free(list);
        list = NULL;
    }
}


Re-using a freed pointer can be a subtle error. Your code keeps right on working, and then crashes for no clear reason because some seemingly unrelated code wrote in the memory that the re-used pointer happens to be pointing at.  This is to prevent that.