A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
A dynamic array is a pointer, a length, and a capacity. The length is how many elements are in use. The capacity is how many elements the allocation can hold. When length reaches capacity, you grow. When length drops far below capacity, you might shrink to reclaim memory. The growth strategy matters for performance. The shrink strategy matters for memory. Getting both right is the difference between a data structure that scales and one that either reallocates constantly or hoards memory.
#include <stdio.h>#include <stdlib.h>typedef struct {int *data;size_t length;size_t capacity;} IntArray;int ia_init(IntArray *a, size_t cap) {a->data = malloc(cap * sizeof(int));if (!a->data) return -1;a->length = 0;a->capacity = cap;return 0;}int ia_push(IntArray *a, int value) {if (a->length == a->capacity) {size_t new_cap = a->capacity * 2;int *tmp = realloc(a->data, new_cap * sizeof(int));if (!tmp) return -1;a->data = tmp;a->capacity = new_cap;}a->data[a->length++] = value;return 0;}int ia_pop(IntArray *a) {if (a->length == 0) return -1;return a->data[--a->length];}void ia_free(IntArray *a) {free(a->data);a->data = NULL;a->length = 0;a->capacity = 0;}int main(void) {IntArray arr;ia_init(&arr, 4);for (int i = 0; i < 10; i++) {ia_push(&arr, i * 10);}printf("Length: %zu, Capacity: %zu\n", arr.length, arr.capacity);printf("Elements: ");for (size_t i = 0; i < arr.length; i++) {printf("%d ", arr.data[i]);}printf("\n");printf("Popped: %d\n", ia_pop(&arr));printf("Popped: %d\n", ia_pop(&arr));printf("Length after pops: %zu\n", arr.length);ia_free(&arr);return 0;}
Started at capacity 4. Grew to 8, then 16. Ten elements fit in a capacity-16 allocation. Two reallocs total for ten pushes. The amortized cost per push is constant. The pop operation does not shrink the allocation. It just decrements the length. The memory stays allocated for future pushes. This is the standard trade-off: you trade some wasted memory for fewer reallocations.
This is the foundation of every growable collection in C. Vectors, lists, hash tables with dynamic buckets. They all use this pattern. Next: increasing the size of a dynamically allocated 2D array, which adds a layer of complexity to the same technique.
Finish the lesson once you have worked through the material. This awards ★ 30 XP.