Handout: Lecture 8 In-Class Exercises¶
Part A is pen-and-paper: rewrite member access through pointers, and reason
about the size of a union. Part B is at the keyboard: change a struct through a
pointer, give a struct a heap-owned string, model a small set of states with a
typedef'd enum, and build a tagged union. None of these repeats an earlier
exercise; they drill today's moves - the -> operator, pointer parameters,
heap-owned members, enum, and union.
Try each one yourself first; we will discuss in class, and the solutions are in a separate document afterward. Compile with warnings on:
Reminders that will keep you out of trouble today:
p->memberis shorthand for(*p).member. Use->when you hold a pointer to a struct, and.when you hold the struct itself.- To change a caller's struct, take a pointer parameter and pass
&s. Addconstto the pointer when the function only reads. - A struct that owns a heap string allocates
strlen(s) + 1bytes (room for the'\0') and needs afree_*function: onemalloc, onefree. - An
enumis namedintconstants, numbered from 0 by default. - A
unionis sized to its largest member and holds one member at a time; pair it with anenumtag and check the tag before reading.
Set up¶
Part A - Pointers and unions (pen and paper)¶
Exercise A1 - Dots and arrows¶
Given these declarations:
- For each expression, say whether it is correct as written, and if not, fix
it:
pt.x,p.x,p->x,(*p).y,*p.y. - Write two different expressions that both set
pt'syto9through the pointerp(do not useptdirectly). - In one sentence: when do you use
.and when do you use->?
Exercise A2 - How big is the union?¶
Assume int is 4 bytes and double is 8 bytes. Consider:
- Roughly how many bytes does
struct sneed, and why? (Think: members side by side.) - Roughly how many bytes does
union uneed, and why? (Think: members on top of one another.) - After
union u x; x.a = 5; x.b = 2.0;, is readingx.ameaningful? Explain in one sentence what auniondoes not track, and how a tagged union fixes it.
(Sizes may be rounded up by the compiler for alignment; give the reasoning, not an exact byte count.)
Part B - At the keyboard¶
Exercise B1 - Deposit and withdraw through a pointer¶
Model a bank account as a struct, and write two functions that change the caller's account in place through a pointer. This is the pointer-to-struct version of an out-parameter.
typedef struct {
char owner[32];
double balance;
} account_t;
void deposit(account_t *a, double amount);
int withdraw(account_t *a, double amount); /* returns 1 on success, 0 if too little */
- In
deposit:a->balance += amount;. Inwithdraw: only subtract ifa->balance >= amount, returning1; otherwise change nothing and return0. - In
main, build an account (e.g.{"Ada", 100.0}), calldeposit(&acct, 30), trywithdraw(&acct, 200), and printacct.balanceafter each. Pass the address; use->inside. - Note the difference from passing by value: these functions see and modify the caller's real account, because they hold its address.
Exercise B2 - A struct that owns a heap string¶
Write a struct that holds a pointer to a heap-allocated label, plus a function
to build one and a function to free it. This is the ownership pattern from the heap
lecture, now living inside a struct and freed through a pointer.
typedef struct {
char *label; /* heap string this struct owns */
int priority;
} tag_t;
tag_t make_tag(const char *label, int priority);
void free_tag(tag_t *t);
- In
make_tag: setpriority, thenmalloc(strlen(label) + 1), check it againstNULL, andstrcpythe characters into your own block. Return the struct by value. (#include <string.h>forstrlen/strcpy.) - In
main: build a tag, printt.labelandt.priority, then callfree_tag(&t). - In
free_tag:free(t->label)and sett->label = NULL, reaching the member with->becausetis a pointer. Onemalloc, onefree. Run undervalgrindto confirm no leak.
Exercise B3 - A traffic light state machine¶
Use a typedef'd enum to model the three states of a traffic light, and write a
function that returns the next state. This drills enums and switch on an enum.
- The cycle is
GREEN -> YELLOW -> RED -> GREEN -> .... Implementnext_lightwith aswitchoncurrent(onecaseper state), or with arithmetic if you prefer. - Add a helper
const char *light_name(light_t l)that returns"GREEN","YELLOW", or"RED"for printing (aswitchreturning a string literal). - In
main, start atGREENand print four states by repeatedly callingnext_light. Notice the enum names make the code read like the intent.
Exercise B4 - A tagged union of measurements (overflow)¶
This exercise goes with the unions section, which is overflow material. Do it in class if we reach unions; otherwise treat it as extra practice and we will pick it up next lecture.
Build a value that is either a temperature in Celsius or a distance in
meters, using an enum tag and a union. A function prints it correctly by
checking the tag.
typedef enum { KIND_TEMP, KIND_DIST } kind_t;
typedef struct {
kind_t kind;
union {
double celsius; /* valid when kind == KIND_TEMP */
double meters; /* valid when kind == KIND_DIST */
};
} measurement_t;
void print_measurement(const measurement_t *m);
print_measurementdoes aswitch (m->kind): forKIND_TEMPprintm->celsiuswith aC, forKIND_DISTprintm->meterswith anm. Read the union member only after checking the tag.- In
main, build one of each:measurement_t t = {.kind = KIND_TEMP, .celsius = 23.5};and a distance the same way. Pass&tto the printer. - The whole point: the union stores only one
doubleworth of space, and the tag is what tells you which meaning those bytes currently have.
Stretch - take it further¶
An array of shapes with a total area¶
Combine everything: a typedef'd tagged union shape_t that is either a circle (with
a radius) or a rectangle (with w and h), plus a function
double total_area(const shape_t *shapes, int n) that sums the areas of an array of
them.
typedef enum { CIRCLE, RECT } shape_kind_t;
typedef struct {
shape_kind_t kind;
union {
double radius; /* CIRCLE */
struct { double w, h; }; /* RECT */
};
} shape_t;
- Write
double shape_area(const shape_t *s)that switches ons->kind(3.14159 * r * rfor a circle,w * hfor a rectangle), then havetotal_arealoop over the array calling it. - Build a small array mixing circles and rectangles with designated initializers, and print the total. This tagged-union-in-an-array is the shape of a great deal of real C: a heterogeneous list whose elements each carry a tag saying what they are.