Skip to content

CMSC 14300: Practice Set 5 - Structs, Enums, and Unions

Practice for the material in Week 4: defining and using structs, arrays of structs, pointers to structs and the -> operator, structs that own heap memory, typedef, enum, and union. None of these repeats a lecture or homework exercise; together they drill the new moves - group fields into a type, reach them with . and ->, change a struct through a pointer, and tag a union. This is good preparation for the next quiz.

Work in a fresh directory and compile everything with warnings on. Some problems need <stdlib.h> (for malloc/free) and <string.h> (for strlen/strcpy):

mkdir -p ~/cmsc14300/lec07-pset && cd ~/cmsc14300/lec07-pset
clang -Wall -Wextra -std=c17 problem1.c -o problem1

Keep last week's memory rules in view: every malloc still has exactly one free, and a struct copy does not copy what a pointer member points at.


Problem 1 - Distance between two points in 3D

Model a point in three dimensions as a struct, and write a function that returns the straight-line distance between two of them. Both points are passed by value, and the answer comes back as a double.

struct point3d { double x; double y; double z; };

double distance(struct point3d a, struct point3d b);
distance = 5.00
  • The distance is sqrt((a.x-b.x)^2 + (a.y-b.y)^2 + (a.z-b.z)^2). Include <math.h> for sqrt, and compile with -lm if your system needs the math library: clang -Wall -Wextra -std=c17 problem1.c -o problem1 -lm.
  • Reach each member with the dot operator (a.x, b.z). The inputs are copies, so nothing you pass is disturbed; the result is returned by value.
  • Check with (0,0,0) and (3,4,0), which are 5 apart.

Problem 2 - The hottest day (array of structs)

Define a struct for a daily temperature reading, fill a small array of them, and report the hottest day and the average temperature. This is a table of records scanned by one of its fields.

struct reading {
    int    day;
    double celsius;
};
hottest: day 3 (31.2 C)
average: 24.65 C
  • Initialize an array such as { {1, 22.5}, {2, 19.0}, {3, 31.2}, {4, 26.0} }.
  • Walk the array once: track the index of the largest celsius (seed from element 0) and sum every celsius into a running double. Index first, then dot: readings[i].celsius.
  • Print the winning day's day and celsius, then the average (sum / n, as a double).
  • Check your understanding: why does readings[i].day reach the day of element i, but readings.day[i] does not even compile?

Problem 3 - Rename a playlist (pointer to struct + heap-owned string)

A struct owns a heap-allocated title. Write a function that replaces that title through a pointer: it frees the old string and installs a fresh copy of the new one. This is the pointer-to-struct move combined with last week's ownership rules.

struct playlist {
    char *title;     /* heap string this struct owns */
    int   length;    /* number of songs */
};

void set_title(struct playlist *p, const char *new_title);
"Focus" (12 songs)
"Deep Focus" (12 songs)
  • In set_title: free(p->title); first (the old string), then p->title = malloc(strlen(new_title) + 1);, check for NULL, and strcpy the new characters in. Use -> because p is a pointer.
  • In main: build a playlist whose title you allocated the same way (or start with a first set_title on a struct whose title is NULL - freeing NULL is safe), print it, rename it, print again, then free(p->title) at the end.
  • Check your understanding: what goes wrong if set_title mallocs the new title but forgets to free the old one first? Which of last week's two bugs is that?
  • Run under valgrind to confirm no leak and no use-after-free.

Problem 4 - Count out the change (typedef + enum)

Use a typedef'd enum to name the U.S. coins, and write a function that returns a coin's value in cents. Then sum an array of coins.

typedef enum { PENNY, NICKEL, DIME, QUARTER } Coin;

int coin_value(Coin c);                 /* cents: 1, 5, 10, 25 */
int total_cents(const Coin *coins, int n);
total: 41 cents
  • Implement coin_value with a switch on the enum (one case per coin returning its cent value). total_cents walks the array and adds up coin_value(coins[i]).
  • In main, build an array like {QUARTER, DIME, NICKEL, PENNY} and print the total. The enum names read like the intent; no bare 0/1/2 literals.
  • Check your understanding: what integer values do PENNY, NICKEL, DIME, QUARTER have by default, and why is that separate from their cent values?

Problem 5 - A cell that is a number or a label (tagged union)

Model a spreadsheet cell that holds either a number or a short text label, using an enum tag and a union. A function prints the cell by checking the tag first.

typedef enum { CELL_NUMBER, CELL_TEXT } CellKind;

typedef struct {
    CellKind kind;
    union {
        double number;      /* valid when kind == CELL_NUMBER */
        char   text[16];    /* valid when kind == CELL_TEXT   */
    };
} Cell;

void print_cell(const Cell *c);
42.50
hello
  • print_cell does a switch (c->kind): for CELL_NUMBER print c->number, for CELL_TEXT print c->text. Read the union member only after checking the tag.
  • In main, build one of each with designated initializers: Cell a = {.kind = CELL_NUMBER, .number = 42.5}; and Cell b = {.kind = CELL_TEXT, .text = "hello"};. Pass &a and &b to the printer.
  • Check your understanding: roughly how large is one Cell - closer to the size of a double or the size of double + char[16]? Why? And what does the kind field record that the union alone cannot?

Self-check

You're ready for the upcoming quiz material if you can, without looking it up:

  • Define a struct, declare a variable of it, and reach its members with .; explain why the definition needs a trailing semicolon.
  • Explain what it means that a struct is a value: what a plain assignment copies, and why a function taking a struct by value cannot change the caller's struct.
  • Reach a member through a pointer with ->, and state what p->x is shorthand for. Say when you take a struct parameter by value, by pointer, and by const pointer.
  • Write a struct that owns a heap string: allocate strlen(s) + 1, copy the characters in, and free it exactly once. Explain why a struct copy does not duplicate the string.
  • Use typedef to drop the struct/enum keyword, and write a small enum used in a switch.
  • Say how a union differs from a struct in size and in how many members are valid at once, and why a tagged union (an enum tag plus a union) is the safe way to use one.