Skip to content

Solutions: Lecture 7 In-Class Exercises

Solutions to the Lecture 7 in-class exercises. Try each exercise yourself before reading these.


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

#include <stdio.h>

int main(void) {
    int n;
    printf("How many people? ");
    if (scanf("%d", &n) != 1 || n < 1 || n > 100) {
        return 1;
    }

    char names[100][32];
    int  ages[100];

    for (int i = 0; i < n; i++) {
        printf("Name and age for person %d: ", i + 1);
        scanf("%31s %d", names[i], &ages[i]);
    }

    for (int i = 0; i < n; i++) {
        printf("%s is %d\n", names[i], ages[i]);
    }
    return 0;
}
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
  • The name and the age of person i live in two separate arrays, tied together only by the index i. Nothing in the code says they belong to the same person; the discipline is entirely on you.
  • %31s reads a whitespace-delimited word into a 32-byte buffer, stopping at 31 characters so there is room for the '\0'. (It does not read names with spaces - that is fine for the warm-up.)
  • This is exactly the fragility a struct removes: bundle name and age into one person_t, and the roster becomes a single array of records (see the array of structs in the notes).

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

#include <stdio.h>

typedef struct {
    char name[32];
    int  age;
} person_t;

int main(void) {
    int n;
    printf("How many people? ");
    if (scanf("%d", &n) != 1 || n < 1 || n > 100) {
        return 1;
    }

    person_t roster[100];

    for (int i = 0; i < n; i++) {
        printf("Name and age for person %d: ", i + 1);
        scanf("%31s %d", roster[i].name, &roster[i].age);
    }

    for (int i = 0; i < n; i++) {
        printf("%s is %d\n", roster[i].name, roster[i].age);
    }
    return 0;
}
  • One array now, not two. roster[i] is a whole person_t; roster[i].name and roster[i].age are its members. The name and age of person i can no longer drift apart, because they live in the same record.
  • The scanf arguments follow the usual rule, now reaching through the struct: roster[i].name is an array, so it already is an address (no &), while roster[i].age is an int, so it still needs &.
  • Adding a double gpa; field means editing the struct and the two loops only - there is no parallel gpas[] array to keep in step. That is the whole point of the struct.

Part A - Struct copy semantics (pen and paper)

Exercise A1 - Predict the output

struct box { int w; int h; };
  1. After struct box c = a;, c is a full, independent copy of a: c.w = 4, c.h = 5. Line (2) sets c.h = 99, which touches only c. So a.h is still 5 and c.h is 99. Assigning one struct to another copies every member into separate storage; the two structs share nothing.

  2. widen receives a copy of a in its parameter b. It prints inside: 14 (the copy's w, 4 + 10), but the caller's a.w is unchanged. After widen returns, a.w is still 4, because widen only ever modified its own copy.

  3. The program prints:

inside: 14
a.w = 4
a.h = 5, c.h = 99

Exercise A2 - Fix the member access

  • (a) p is a struct, not a pointer, so use the dot operator, not ->: int sum = p.x + p.y;. (-> is for pointers to structs, next lecture.)
  • (b) seg.a is a whole struct point, not an int; you must select a member of it. The intended value is the difference of the xs: int dx = seg.b.x - seg.a.x;. Chain the dots.
  • (c) Index the array first, then dot into the element. pts.x[0] is backwards. Write int total = pts[0].x + pts[1].x;.

Part B - Structs at the keyboard

Exercise B1 - Add two fractions, return a struct

#include <stdio.h>

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

void print_fraction(fraction_t f) {
    printf("%d/%d", f.num, f.den);
}

fraction_t add_fraction(fraction_t a, fraction_t b) {
    fraction_t result;
    result.num = a.num * b.den + b.num * a.den;
    result.den = a.den * b.den;
    return result;
}

int main(void) {
    fraction_t a = {1, 2};
    fraction_t b = {1, 3};
    fraction_t sum = add_fraction(a, b);

    print_fraction(a);
    printf(" + ");
    print_fraction(b);
    printf(" = ");
    print_fraction(sum);
    printf("\n");
    return 0;
}
1/2 + 1/3 = 5/6
  • add_fraction builds a new fraction_t from the arithmetic and returns it by value. Both inputs are copies, so nothing the caller passed is disturbed.
  • Returning a struct by value is safe: the caller receives a copy, not a pointer into add_fraction's dead frame. No heap, no pointers needed.
  • print_fraction also takes its argument by value; it only reads f, so a copy is fine.

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

#include <stdio.h>

typedef struct {
    char   name[32];
    double price;
} item_t;

int main(void) {
    item_t items[4] = {
        {"pen", 1.50},
        {"keyboard", 49.99},
        {"notebook", 4.99},
        {"mouse", 25.99},
    };
    int n = 4;

    int best = 0;
    double total = items[0].price;
    for (int i = 1; i < n; i++) {
        if (items[i].price > items[best].price) {
            best = i;
        }
        total += items[i].price;
    }

    printf("most expensive: %s (%.2f)\n", items[best].name, items[best].price);
    printf("total: %.2f\n", total);
    return 0;
}
most expensive: keyboard (49.99)
total: 82.47
  • Each element is a whole record; items[i].price and items[i].name reach into element i. Index first, then dot.
  • The max-scan is the familiar one, seeded from element 0, but the key is a field of the record and the answer is the record's index. The running total is summed in the same pass.

Stretch

A running total of records

double inventory_value(item_t *items, int n) {
    double total = 0.0;
    for (int i = 0; i < n; i++) {
        total += items[i].price;
    }
    return total;
}

/* in main: */
printf("total: %.2f\n", inventory_value(items, 4));
  • item_t *items is the array decayed to a pointer to its first element, just like int a[] becomes int *. items[i] is *(items + i), a whole record, and items[i].price reaches its price.
  • Passing the array hands over one pointer, not a copy of every record. This keeps the data in main and the operation in the function - the same separation you have used for int arrays, now over records.