Skip to content

Handout: Lecture 5 In-Class Exercises

Part A is pen-and-paper: read pointer code and draw the boxes and arrows. Part B is at the keyboard: pointers in real programs - walking an array, swapping through addresses, and returning two results with out-parameters. None of these repeats a Lecture 4 exercise; they drill the new move - handing a function an address so it can reach back to your variables.

Try each one yourself first; we will discuss in class, and the solutions are in a separate document afterward. Compile with warnings on:

clang -Wall -Wextra -std=c17 myprog.c -o myprog

Reminders that will keep you out of trouble today:

  • &x is the address of x; *p is the thing p points at. The & at a call site and the * inside the function are two halves of one handshake.
  • p + 1 steps by one element, not one byte - the pointer's type sets the stride. a[i] is the same thing as *(a + i).
  • A function gets a copy of each argument. To change a caller's variable, it needs the variable's address, not its value.
  • Never dereference a pointer that points at nothing (NULL) or at a variable that has already gone out of scope.

Set up

mkdir -p ~/cmsc14300/lec05
cd ~/cmsc14300/lec05

Part A - Read the pointers (pen and paper)

Exercise A1 - Trace the boxes and arrows

Work these out by hand; do not compile them yet. For the snippet below, draw a box for x and a box for p, draw the arrow, and write down what each expression evaluates to.

int x = 10;
int *p = &x;
*p = *p + 5;
  1. After int *p = &x;, what does p hold - a number, or x's value?
  2. What is *p just before the third line runs?
  3. After the third line, what is x? What is *p?
  4. If &x is the address 2000, what does p print with %p? What does &p print - the same thing or something different?

  5. Stretch: add int y = 7; p = &y; at the end. Redraw the arrow. Did x change? What is *p now?


Part B - Pointers at the keyboard

Exercise B1 - Sum an array with a walking pointer

Sum an integer array using a pointer that walks from the start to one past the end. No [] anywhere in your loop - use *p and p++.

int a[] = {10, 20, 30, 40, 50};
int n = 5;
sum = 150
  • Start int *p = a; and loop while p < a + n, doing total += *p; then p++.
  • a + n is the legal "one past the end" marker - you may compare against it, but never dereference it.
  • Stretch: print each element's address alongside its value with %p, and confirm the addresses go up by sizeof(int) each step.

Exercise B2 - Swap through addresses

Write a function that swaps two ints in the caller, then prove it from main.

void swap(int *a, int *b);
before: x = 3, y = 7
after:  x = 7, y = 3
  • Inside swap, the values live at *a and *b; use a temporary int t.
  • Call it as swap(&x, &y);. The classic mistake is swapping the pointers themselves (int *t = a; a = b; b = t;) - that shuffles the local copies and leaves x and y untouched. Make sure your stars are on the values.
  • Stretch: write void swap_bad(int a, int b) that takes plain ints, call it, and confirm x and y do not change. Explain in one sentence why.

Exercise B3 - Two results with out-parameters

A function returns one value with return. To hand back two, pass pointers for it to fill. Write divide so the caller gets both the quotient and the remainder.

void divide(int a, int b, int *quotient, int *remainder);
17 / 5 = 3 remainder 2
  • In main, declare int q, r;, call divide(17, 5, &q, &r);, then print q and r.
  • Inside divide, write the results through the pointers: *quotient = a / b; and *remainder = a % b;.
  • Stretch: this is exactly scanf's pattern - it returns a status and sends your data back through the pointers you pass. Print divide's results for a few input pairs and check quotient * b + remainder == a.

Stretch - take it further

minmax with out-parameters

Combine Part B's two ideas: pass an array and two out-parameters, and report both extremes in a single pass.

void minmax(const int *a, int n, int *lo, int *hi);
  • Seed *lo and *hi from a[0] (the same "seed from an element" rule as before), then walk the rest, updating through the pointers.
  • The const on a is a promise that minmax only reads the array - a good habit whenever a function should not modify what it is handed.
  • Call it with int lo, hi; minmax(a, n, &lo, &hi); and print both. This returns two values from one pass, which a plain return cannot do.