Small implementation of a singly-linked list in C89
init_list

should really initialize the list. Otherwise you are forcing the caller to make an non-obvious memset.

void init_list(linked_list * list) {
    list-&gt;head = 0;
    list-&gt;size = 0;
}


is much more clean.
add_last

is not a particularly good name. Traditionally this operation is called append or push_back.

The function can be streamlined. First, assert your invariant fully (that is either head == 0 and size == 0 or head != 0 and size &gt; 0) and immediately.

Second, once the invariant is deemed correct, allocate the node (you need to do it anyway). It is a good idea to delegate node allocation to a function.

Finally, find the tail (it is also a good candidate to factor out into a function), and attach the node. For example,

void append (linked_list * list, int32_t item)
{
    assert(list_is_good(list));
    linked_list_node * node = create_list_node(item);
    linked_list_node * tail = find_list_tail(list);
    if (tail == NULL) {
        list-&gt;head = node;
    } else {
        tail-&gt;next = node;
    }
    list-&gt;size += 1;
}

malloc may fail

Test the results of malloc. There is not much you can do once it fails, but it is good form anyway.