Code of the Day
IntermediateMemory management

Lab: Dynamic array (growable buffer)

Implement a growable dynamic array in C using malloc and realloc — the foundation of every dynamic collection in systems programming.

Lab · optionalCIntermediate35 min
Recommended first

A dynamic array (also called a vector or growable buffer) is one of the most important in systems programming. It stores elements in a contiguous block of memory (like a fixed array) but can grow as you add elements. This is how Python's list, C++'s std::vector, and Go's slices work internally.

Goal

Implement a DynArray that:

  1. Starts with capacity for 4 elements.
  2. Doubles its capacity with realloc when full.
  3. Supports push (append), get (index access), len (current count), and free_array (cleanup).

The struct

typedef struct {
    int   *data;     /* heap-allocated array */
    int    len;      /* number of elements currently stored */
    int    capacity; /* allocated capacity */
} DynArray;

The API

void da_init(DynArray *da);
int  da_push(DynArray *da, int value); /* returns 0 on success, -1 on failure */
int  da_get(const DynArray *da, int index);
int  da_len(const DynArray *da);
void da_free(DynArray *da);

Implementation hints

da_init: Set data = NULL, len = 0, capacity = 0. Lazy allocation (allocate on first push) keeps init simple.

da_push: If len == capacity, grow. The growth strategy: if capacity == 0, set to 4; otherwise double. Use realloc with a temporary pointer — never assign directly to da->data.

da_get: Bounds-check the index and return da->data[index].

da_free: Call free(da->data) and reset fields to 0/NULL.

Worked solution

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int *data;
    int  len;
    int  capacity;
} DynArray;

void da_init(DynArray *da) {
    da->data = NULL;
    da->len = 0;
    da->capacity = 0;
}

int da_push(DynArray *da, int value) {
    if (da->len == da->capacity) {
        int new_cap = (da->capacity == 0) ? 4 : da->capacity * 2;
        int *tmp = realloc(da->data, new_cap * sizeof(int));
        if (tmp == NULL) { return -1; }
        da->data = tmp;
        da->capacity = new_cap;
    }
    da->data[da->len++] = value;
    return 0;
}

int da_get(const DynArray *da, int index) {
    if (index < 0 || index >= da->len) {
        fprintf(stderr, "Index %d out of bounds (len=%d)\n", index, da->len);
        exit(1);
    }
    return da->data[index];
}

int da_len(const DynArray *da) {
    return da->len;
}

void da_free(DynArray *da) {
    free(da->data);
    da->data = NULL;
    da->len = 0;
    da->capacity = 0;
}

int main(void) {
    DynArray arr;
    da_init(&arr);

    for (int i = 0; i < 20; i++) {
        if (da_push(&arr, i * i) != 0) {
            fprintf(stderr, "push failed\n");
            da_free(&arr);
            return 1;
        }
    }

    printf("Length: %d\n", da_len(&arr));
    for (int i = 0; i < da_len(&arr); i++) {
        printf("%d ", da_get(&arr, i));
    }
    printf("\n");

    da_free(&arr);
    return 0;
}

Verify with valgrind

gcc -g -O0 dynarray.c -o dynarray
valgrind --leak-check=full ./dynarray

Expected: zero errors, zero leaks.

Extension challenges

  1. Add a da_remove function that removes an element by index and shifts the remaining elements left.
  2. Store strings. Change int *data to char **data and make da_push take a const char *, duplicating the string with strdup (or your own my_strdup). da_free must then free each string before freeing the array.
  3. Shrink policy. When len < capacity / 4, realloc down to capacity / 2. This prevents unbounded memory use after a large array is trimmed.

What you practised

  • Dynamic allocation with malloc and realloc
  • The safe realloc pattern (temp pointer, NULL check, then assign)
  • Struct-based data structures with a consistent API
  • The doubling growth strategy and why it gives amortized O(1) push

The next module is Structs and enums — grouping related data into custom types, which is the foundation of every data structure you will build in C.

Finished reading? Mark it complete to track your progress.

On this page