Handout: Lecture 6 In-Class Exercises¶
Part A is pen-and-paper: trace a recursion's stack frames and spot a
dangling-pointer bug by reading. Part B is at the keyboard: allocate memory on
the heap, use it, and free it - first a dynamic array, then a function that hands a
heap array back to its caller. None of these repeats an earlier exercise; they
drill the new move - malloc it, check it, use it, free it once.
Try each one yourself first; we will discuss in class, and the solutions are in a
separate document afterward. Compile with warnings on, and link <stdlib.h> for
malloc/free:
Reminders that will keep you out of trouble today:
- A local variable lives only while its stack frame is on the stack - it dies when the function returns. Never return the address of a local.
- Every
mallochas exactly onefree. Zero frees leaks; two frees is a double-free. Pair them as you write themalloc. - Check
malloc's result againstNULLbefore you dereference it. - After
free(p), the block is no longer yours. Setp = NULL;so a stray use crashes loudly instead of corrupting memory. - Size an allocation as count times element size:
malloc(n * sizeof(int)). Never hard-code the byte count.
Set up¶
Part A - Stacks and lifetimes (pen and paper)¶
Exercise A1 - Trace the frames¶
Do not compile this; trace it by hand. Consider:
- Draw the stack frames for
sum_to(3). How many frames are on the stack at the deepest point, and what is each frame'sn? - In what order do the frames return? Write the value each one returns.
- Each call has its own
n. Explain in one sentence why the threenvalues do not collide.
Exercise A2 - Spot the bug¶
Each snippet has one memory bug. Name it (dangling pointer / use-after-free / double-free / memory leak / returning a local's address) and say how to fix it.
/* (a) */
int *first(void) {
int x = 10;
return &x;
}
/* (b) */
void run(void) {
int *p = malloc(sizeof(int));
*p = 5;
free(p);
printf("%d\n", *p);
}
/* (c) */
void load(int n) {
int *a = malloc(n * sizeof(int));
/* ... fills and uses a ... */
return;
}
Part B - The heap at the keyboard¶
Exercise B1 - A dynamic array¶
Read a count n at run time, allocate exactly n ints on the heap, fill them from
input, report something about them, and free the block. This is the array Lecture 4
could not give you - its size is chosen while the program runs.
- Allocate with
int *a = malloc(n * sizeof(int));and check it againstNULLbefore using it. - Fill with a
scanf("%d", &a[i])loop, then walk the array to find the largest (seed froma[0]). - End with exactly one
free(a);. There is onemalloc, so there is onefree. - Stretch: also report the average as a
double(cast before dividing). The two facts - a runtime size and a heap block - are the only new things here; the array logic is the same as always.
Exercise B2 - Return a heap array¶
Write a function that builds and returns a heap array of the first n squares,
then have main use it and free it. This is the safe version of "returning a
local's address": the heap block outlives the call.
- Inside
make_squares:malloc(n * sizeof(int)), check forNULL(returnNULLon failure), filla[i] = i * i;, andreturn a;. - In
main: call it, check the result, print the values, thenfreeit -mainnow owns the block. The function allocated it; the caller frees it. - Stretch: write the broken sibling
int *make_squares_bad(int n)that declares a localint a[100];and returnsa;. Note the compiler warning about returning the address of a local, and explain why B2's heap version is safe where this one is not.
Stretch - take it further¶
Concatenate two arrays onto the heap¶
Write a function that allocates a brand-new array big enough to hold two input arrays end to end, copies them in, and returns it.
- Allocate
(na + nb) * sizeof(int), check forNULL, copya'snaelements thenb'snbelements into the new block, and return it. - The caller owns the result and must
freeit. Print the joined array frommainto check, e.g.{1, 2, 3}and{4, 5}give1 2 3 4 5. - The
conston the inputs promisesconcatonly reads them; only the new block is written. This "allocate, fill, return, caller frees" shape is how real C code builds up data whose size it computes at run time.