DMarlon Studio

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.

C · Structs with pointers on the heap in DMarlon Studio: the example's code and its output in the terminal

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) and strcpy.
  • p->field is (*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.