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
typedefgives the struct a short name so you can drop the wordstruct:typedef struct { ... } thing_t;, then declarething_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¶
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.
- Ask how many people there are and read the count into an
int n(assume1 <= n <= 100). - Loop
ntimes, reading each person's name and age into the array. - 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.
- Define the type:
typedef struct { char name[32]; int age; } person_t;. - Declare one array,
person_t roster[100];, in place of the two separatenamesandagesarrays. - In the read loop, read into
roster[i].nameandroster[i].age. - In the print loop, print
roster[i].nameandroster[i].age. The output is identical to the Warm-up. - Stretch: give
person_ta 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;
}
- After line
(1)and(2), what area.handc.h? - What does the
inside:line print at(3), and what isa.wafterwidenreturns? Explain in one sentence whywidendid not changea. - 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);
- The sum of
a/bandc/dis(a*d + c*b) / (b*d). Build afraction_twith those two values andreturnit. - 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 printresult.numandresult.den. - Stretch: add a helper
void print_fraction(fraction_t f)that printsn/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.
- Initialize an array of three or four
item_tvalues, 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 eachpriceinto a runningdouble total. - Index the array first, then dot into the element:
items[i].price,items[i].name. Print the winner'snameandprice, 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 *itemsis the array decayed to a pointer, exactly likeint a[]becameint *. Inside,items[i].pricereads the price of elementi. - This separates the data (the array in
main) from the operation (the function), the same split you have used forintarrays - only now each element is a full record. Passing the array does not copy the records; passing a singleitem_tby value would.