A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
A dynamic array of structs is a pointer to struct, allocated with malloc, indexed with array notation. You allocate n elements of sizeof(StructType). You access elements with arr[i].member. You grow with realloc. You free with a single free call because the elements are contiguous. This is the C equivalent of a vector of objects, and it is the backbone of every in-memory database, every entity-component system, and every record store written in C.
#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct {int id;char name[32];double score;} Student;typedef struct {Student *data;size_t length;size_t capacity;} StudentList;int list_init(StudentList *list, size_t cap) {list->data = malloc(cap * sizeof(Student));if (!list->data) return -1;list->length = 0;list->capacity = cap;return 0;}int list_push(StudentList *list, int id, const char *name, double score) {if (list->length == list->capacity) {size_t new_cap = list->capacity * 2;Student *tmp = realloc(list->data, new_cap * sizeof(Student));if (!tmp) return -1;list->data = tmp;list->capacity = new_cap;}Student *s = &list->data[list->length];s->id = id;strncpy(s->name, name, sizeof(s->name) - 1);s->name[sizeof(s->name) - 1] = '\0';s->score = score;list->length++;return 0;}void list_free(StudentList *list) {free(list->data);list->data = NULL;list->length = 0;list->capacity = 0;}int main(void) {StudentList students;list_init(&students, 2);list_push(&students, 1, "Alex", 95.5);list_push(&students, 2, "Jordan", 87.0);list_push(&students, 3, "Sam", 92.3);printf("Students (len=%zu, cap=%zu):\n", students.length, students.capacity);for (size_t i = 0; i < students.length; i++) {printf(" #%d %-10s %.1f\n",students.data[i].id,students.data[i].name,students.data[i].score);}list_free(&students);return 0;}
Started at capacity 2. The third push triggered a realloc to capacity 4. The elements are contiguous in memory. A single free releases the entire array. The -> and . operators work as expected: list->data accesses the pointer through the list pointer, and students.data[i].name accesses the member through the array index. The growth pattern is identical to the dynamic int array from the pointers module, just with sizeof(Student) instead of sizeof(int).
Dynamic struct arrays are the workhorse of C data management. One allocation. Contiguous memory. Cache-friendly traversal. Simple cleanup. The pattern scales from ten records to ten million. Next: structure padding, the hidden bytes that make your structs larger than you expect.
Finish the lesson once you have worked through the material. This awards ★ 30 XP.