Lesson 8 · 8 of 20
Structs with pointers on the heap · C
Create an array of structs on the heap in C whose fields are dynamic memory too, and free all of it in the right order.

Player does not hold its data inside: it has pointers to it. name points to a string and scores to an array of count scores. That way each player takes only what it needs: Lucia has two scores and Martina has four.
copy_text makes a heap copy of a string: it allocates strlen(s) + 1 bytes (the +1 is for the '\0') and copies with strcpy. It is needed because strdup is not part of standard C11, even though many systems provide it.
malloc(n * sizeof *team) creates the array of structs on the heap. Each team[i] is a whole Player, and its fields are filled with further allocations: one for the name and one for the scores. If any of them returns NULL, the program stops.
The output loop walks the array with a pointer p: p->name is the same as (*p).name, and p < team + n stops just after the last player.
Freeing goes from the inside out: first what each struct owns (name and scores), then the team array. Freeing team first would leave you unable to read where the names were, and they would be lost.
Code · main.c
#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct { char *name; int *scores; int count; } Player;// A heap copy of a string (strdup is not part of standard C11).char *copy_text(const char *s) { char *copy = malloc(strlen(s) + 1); // +1 for the final '\0' return copy ? strcpy(copy, s) : NULL;}int main(void) { const char *names[] = {"Lucia", "Hugo", "Martina"}; int n = 3; Player *team = malloc(n * sizeof *team); // an array of structs on the heap if (!team) return 1; for (int i = 0; i < n; i++) { team[i].name = copy_text(names[i]); team[i].count = i + 2; // each player has a different size team[i].scores = malloc(team[i].count * sizeof(int)); if (!team[i].name || !team[i].scores) exit(EXIT_FAILURE); for (int k = 0; k < team[i].count; k++) team[i].scores[k] = 10 + (i * 5 + k * 3) % 9; } for (Player *p = team; p < team + n; p++) { // p->name is (*p).name int total = 0; printf("%-8s", p->name); for (int k = 0; k < p->count; k++) { printf(" %2d", p->scores[k]); total += p->scores[k]; } printf(" | total %d\n", total); } for (int i = 0; i < n; i++) { free(team[i].name); free(team[i].scores); } free(team); // inside out: first what each struct owns, then the array return 0;}Output · cc main.c -o main && ./main
Lucia 10 13 | total 23 Hugo 15 18 12 | total 45 Martina 11 14 17 11 | total 53
Key points
- A struct can hold pointers to memory it owns.
- Copy strings with
malloc(strlen(s) + 1)andstrcpy. p->fieldis(*p).field.- Free from the inside out: the fields, then the array.
Exercise
Write add_score(Player *p, int score), which grows scores with realloc and appends a score, and use it to give Hugo 20 more points.