Skip to content

Handout: Lecture 7 In-Class Exercises

The Warm-up comes first, at the keyboard and before structs: build a roster with parallel arrays so you feel the problem structs solve. Part A is pen-and-paper: reason about struct copy semantics by predicting output. Part B is at the keyboard: define your own structs and use them - a struct returned by value and an array of records. None of these repeats an earlier exercise; they drill the new moves - group fields into a type and reach them with the dot operator.

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:

  • A struct definition ends with a semicolon after the closing brace.
  • A typedef gives the struct a short name so you can drop the word struct: typedef struct { ... } thing_t;, then declare thing_t x;. We suffix these type names with _t. The skeletons below use this form.
  • Reach a member with the dot operator: p.x. For nested structs, chain it: box.top_left.x. For an array, index first: roster[i].age.
  • A struct is a value: assigning or passing one copies every member. A function that takes a struct by value cannot change the caller's struct.

Set up

mkdir -p ~/cmsc14300/lec07
cd ~/cmsc14300/lec07

Warm-up - The roster, the hard way (parallel arrays)

Do this before we introduce structs. Keep a small roster where each person has a name and an age, using two parallel arrays held together by a shared index.

  1. Ask how many people there are and read the count into an int n (assume 1 <= n <= 100).
  2. Loop n times, reading each person's name and age into the array.
  3. Loop again and print each person as name is age.
How many people? 2
Name and age for person 1: Ada 36
Name and age for person 2: Grace 79
Ada is 36
Grace is 79
  • Consider ways in which you can read a name with spaces in it (e.g., Ada Lovelace).

Warm-up, continued - The roster, with a struct

Come back to this once we have covered structs in class. Take your Warm-up program and replace the two parallel arrays with a single array of a person_t struct - the same roster, but now each name and age travel together as one record.

  1. Define the type: typedef struct { char name[32]; int age; } person_t;.
  2. Declare one array, person_t roster[100];, in place of the two separate names and ages arrays.
  3. In the read loop, read into roster[i].name and roster[i].age.
  4. In the print loop, print roster[i].name and roster[i].age. The output is identical to the Warm-up.
  5. Stretch: give person_t a third field, e.g. double gpa;, and read and print it too. Notice you touch only the struct definition and the two loops - there is no third array to thread through, which is exactly the fragility the struct removed.

Same input and output as the Warm-up.


Part A - Struct copy semantics (pen and paper)

Exercise A1 - Predict the output

Do not compile this yet; work out the output by hand, then check.

#include <stdio.h>

struct box { int w; int h; };

void widen(struct box b) {
    b.w = b.w + 10;
    printf("inside: %d\n", b.w);
}

int main(void) {
    struct box a = {4, 5};
    struct box c = a;      /* (1) */
    c.h = 99;              /* (2) */

    widen(a);              /* (3) */
    printf("a.w = %d\n", a.w);
    printf("a.h = %d, c.h = %d\n", a.h, c.h);
    return 0;
}
  1. After line (1) and (2), what are a.h and c.h?
  2. What does the inside: line print at (3), and what is a.w after widen returns? Explain in one sentence why widen did not change a.
  3. Write the two lines the program prints after the call. Then compile and confirm.

Exercise A2 - Fix the member access

Each snippet has one mistake in how it reaches a struct member. Say what is wrong and write the correction.

struct point { int x; int y; };
struct line  { struct point a; struct point b; };

/* (a) */
struct point p = {1, 2};
int sum = p->x + p->y;

/* (b) */
struct line seg = { {0, 0}, {3, 4} };
int dx = seg.b.x - seg.a;

/* (c) */
struct point pts[3] = { {1,1}, {2,2}, {3,3} };
int total = pts.x[0] + pts.x[1];

Part B - Structs at the keyboard

Exercise B1 - Add two fractions, return a struct

Model a fraction as a struct with a numerator and a denominator, and write a function that adds two fractions and returns the result as a struct (by value). Do not worry about reducing to lowest terms.

typedef struct { int num; int den; } fraction_t;

fraction_t add_fraction(fraction_t a, fraction_t b);
1/2 + 1/3 = 5/6
  • The sum of a/b and c/d is (a*d + c*b) / (b*d). Build a fraction_t with those two values and return it.
  • Both inputs are passed by value (copies), and the result is returned by value - no pointers and no heap here. In main, build two fractions, call the function, and print result.num and result.den.
  • Stretch: add a helper void print_fraction(fraction_t f) that prints n/d, and reuse it for all three fractions.

Exercise B2 - An inventory of items (array of structs)

Define a struct for a store item with a name and a price, fill a small array of them, and report the most expensive item and the total price. This is a table of records scanned by a field.

typedef struct {
    char   name[32];
    double price;
} item_t;
most expensive: keyboard (49.99)
total: 82.47
  • Initialize an array of three or four item_t values, e.g. { {"pen", 1.50}, {"keyboard", 49.99}, ... }.
  • Walk the array once. Track the index of the largest price (seed from element 0), and add each price into a running double total.
  • Index the array first, then dot into the element: items[i].price, items[i].name. Print the winner's name and price, then the total.

Stretch - take it further

A running total of records

Extend Exercise B2: write a function double inventory_value(item_t *items, int n) that takes the array and its length and returns the summed price of all n items, then call it from main.

  • The parameter item_t *items is the array decayed to a pointer, exactly like int a[] became int *. Inside, items[i].price reads the price of element i.
  • This separates the data (the array in main) from the operation (the function), the same split you have used for int arrays - only now each element is a full record. Passing the array does not copy the records; passing a single item_t by value would.