Class 12 — Thursday, October 1

A calculator and the swap that works.

1 A calculator, in stages

For anyone with nothing to correct, and anyone who finishes. In pairs; swap who types at every Done when.

Your calculator reads one expression with no spaces, such as 12+34, and walks it to its '\0', as stars.c did. This starter prints each character on its own line:

#include <stdio.h>
#define SIZE 100

int main() {
    char line[SIZE];
    scanf("%99s", line);
    for (int i = 0; line[i] != '\0'; i++) {
        char c = line[i];
        printf("%c\n", c);
    }
}

One new fact: a character that is a digit becomes its number with c - '0', so '7' - '0' is 7.

1.1 Two numbers

12+34 prints 46, and 7-10 prints -3. Build each number one digit at a time: multiply what you have so far by 10, then add the new digit.

Done when both print as shown.

1.2 Any length

12+34-5+100 prints 141. Keep a running total, and apply each operator when you reach the next one.

Done when it prints 141.

1.3 Times and divide

Add * and /, still working from left to right: 2+3*4 prints 20, and 100/7*7 prints 98.

Done when both print as shown, and you can say why the second is not 100.

1.4 Precedence

Now make * and / happen before + and -: 2+3*4 prints 14, 10-2*3 prints 4, and 100/7*7 still prints 98. Keep two numbers: the total of the finished parts, and the part you are still multiplying.

Done when all three print as shown.

1.5 Break it

Try 7/0, 99999*99999 and 12+. For each one, add a comment line to your file: what happened, and why.

Done when the file has all three comments.

1.6 Fix what you broke

Print cannot divide by zero for 7/0, and missing number for 12+, instead of crashing or guessing.

Done when both print those words.

1.7 Many expressions, from a file

scanf returns how many things it read, so while (scanf("%99s", line) == 1) runs once for each expression until there are none left. Put five expressions in exprs.txt, one per line, and run:

./calc < exprs.txt

Done when one run prints all five answers.

2 The swap that works

2.1 The one that did nothing

From A3. Predict what it prints, then run it.

#include <stdio.h>
#include <assert.h>

void swap(int a, int b) {
    int t = a;
    a = b;
    b = t;
}

int main() {
    int x = 3;
    int y = 8;
    printf("before: x = %d, y = %d\n", x, y);
    swap(x, y);
    printf("after:  x = %d, y = %d\n", x, y);
    assert(x == 8);
    assert(y == 3);
}

2.2 Give it the addresses

#include <stdio.h>
#include <assert.h>

// swaps the two variables that a and b point to
void swap(int * a, int * b) {
    int t = *a;
    *a = *b;
    *b = t;
}

int main() {
    int x = 3;
    int y = 8;
    printf("before: x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("after:  x = %d, y = %d\n", x, y);
    assert(x == 8);
    assert(y == 3);
}

Predict: what does the after: line print now?

  • &x is the address of x: where x lives in memory.
  • int * a makes a a pointer: a variable that holds the address of an int.
  • *a follows that address: it is the variable a points to.

Every scanf("%d", &n) you have written used the same &: scanf has to know where n lives in order to put the number there.

2.3 Where things live

#include <stdio.h>

int main() {
    int x = 3;
    int * p = &x;
    printf("x is %d, and lives at %p\n", x, &x);
    printf("p holds %p, and *p is %d\n", p, *p);
}

Predict: do the two lines print the same address?

2.4 Without the &

Change the call in the working swap to swap(x, y);. Predict first: does it compile?

3 Pointers, in pairs

In pairs; swap who types at every Done when. Every function gets the design recipe’s one-line comment, and every test at least one assert of your own.

3.1 twice

Write void twice(int * p), which doubles the variable that p points to. Start from int n = 21;, call twice(&n);, and assert(n == 42);.

Done when the assert passes.

3.2 shift

Write void shift(int * a, int * b, int * c), which moves each value one variable to the right, and the last one round to the first:

int main() {
    int x = 1;
    int y = 2;
    int z = 3;
    shift(&x, &y, &z);
    assert(x == 3);
    assert(y == 1);
    assert(z == 2);
}

Done when these pass, and so do your own asserts that three shifts bring all three values back to where they started.

3.3 One step of Fibonacci

Write void fib_step(int * a, int * b), which turns the pair a, b into b, a + b. From 0, 1, one step gives 1, 1, and ten steps give 55, 89:

#define STEPS 10

int main() {
    int a = 0;
    int b = 1;
    for (int i = 0; i < STEPS; i++) {
        fib_step(&a, &b);
    }
    assert(a == 55);
    assert(b == 89);
}

Done when both pass.

3.4 A swap with no spare variable

This swap needs no third variable:

// swaps the two variables that a and b point to, without a third variable
void swap2(int * a, int * b) {
    *a = *a + *b;
    *b = *a - *b;
    *a = *a - *b;
}

Check that it swaps x and y. Then call swap2(&x, &x);. Predict first: what is x afterwards? Then make the same call with the swap from section 2.2.

Done when you can say why the two swaps disagree.

3.5 The address of the bigger one

A function can return an address too. Write int * bigger(int * a, int * b), which returns the address of whichever variable holds the larger value. Then this line sets the larger of x and y to zero, whichever one it is:

int main() {
    int x = 4;
    int y = 9;
    *bigger(&x, &y) = 0;
    assert(x == 4);
    assert(y == 0);
}

Done when it passes, and still does with the values of x and y the other way round.

4 Before you leave