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
ilive in two separate arrays, tied together only by the indexi. Nothing in the code says they belong to the same person; the discipline is entirely on you. %31sreads 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
structremoves: bundlenameandageinto oneperson_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 wholeperson_t;roster[i].nameandroster[i].ageare its members. The name and age of personican no longer drift apart, because they live in the same record. - The
scanfarguments follow the usual rule, now reaching through the struct:roster[i].nameis an array, so it already is an address (no&), whileroster[i].ageis anint, so it still needs&. - Adding a
double gpa;field means editing the struct and the two loops only - there is no parallelgpas[]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¶
-
After
struct box c = a;,cis a full, independent copy ofa:c.w = 4,c.h = 5. Line(2)setsc.h = 99, which touches onlyc. Soa.his still5andc.his99. Assigning one struct to another copies every member into separate storage; the two structs share nothing. -
widenreceives a copy ofain its parameterb. It printsinside: 14(the copy'sw,4 + 10), but the caller'sa.wis unchanged. Afterwidenreturns,a.wis still4, becausewidenonly ever modified its own copy. -
The program prints:
Exercise A2 - Fix the member access¶
- (a)
pis 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.ais a wholestruct point, not anint; you must select a member of it. The intended value is the difference of thexs: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. Writeint 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;
}
add_fractionbuilds a newfraction_tfrom 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_fractionalso takes its argument by value; it only readsf, 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;
}
- Each element is a whole record;
items[i].priceanditems[i].namereach into elementi. 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
totalis 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 *itemsis the array decayed to a pointer to its first element, just likeint a[]becomesint *.items[i]is*(items + i), a whole record, anditems[i].pricereaches its price.- Passing the array hands over one pointer, not a copy of every record. This keeps
the data in
mainand the operation in the function - the same separation you have used forintarrays, now over records.