Skip to content

Variables, assignments and a trace

Lesson 1 of 1312 minPDF notesFree

What will this program print?

C
#include <stdio.h>

int main(void)
{
    int x = 6;
    int y = 4;
    x = x + 3;
    y = x - y;
    printf("%d %d\n", x, y);
    return 0;
}

The two numbers are not obvious until we follow the changes. By the end of this lesson, you will be able to name the value held by each variable after every statement and explain the printed result.

Read the small amount of syntax we need

A variable is a named place for a value. Here, x and y each hold an int: an integer such as 6, 0 or -3. int x = 6; declares x and initializes it to 6. Initialization supplies its first value. Later, x = x + 3; changes the value of the existing variable; it does not declare another x.

The semicolon ends each declaration or expression statement shown here. The braces surround the body of main. int main(void) is the entry function for these complete programs; void here says it takes no parameters. #include <stdio.h> supplies the declaration needed for printf. The include line does not end in a semicolon. return 0; ends this successful execution. You can trace the statements inside the braces before learning functions in detail. A declaration may also introduce several variables of the same type: int a=8, b=5; gives a the value 8 and b the value 5. The comma separates the variable names and their initializers in that declaration.

printf("%d %d\n", x, y); prints two integer values. The first %d corresponds to x, the second to y. The space inside the quotes separates the numbers, and \n ends the output line. The quotes, %d, and \n themselves are not printed. In this call both arguments merely read existing values; neither changes a variable. The order in which a C implementation evaluates these arguments therefore cannot change this result.

Only ordinary whole-number arithmetic is needed: for example, 17 - 9 is 8, and 6 * 4 is 24. No earlier programming knowledge is assumed. In the code here, * means multiplication; the letter x is a variable name, not a multiplication sign.

The module's code contract

We use ISO C11 as our explicit teaching convention. Every deterministic example starts its variables with known values, uses ordinary signed int, and keeps every intermediate result between -32767 and 32767. All evaluated divisors are nonzero. These choices avoid relying on a particular machine's integer size or representation. We do not assume that int is 32 bits. Inputs are the values written in the declarations; no keyboard input is required.

State: the values that exist now

The state is the collection of current variable values at a particular point. A trace is a written record of those states. Make a new record after each statement. If a statement does not assign to a variable, carry that variable's value forward unchanged.

For an assignment of the simple form x = x + 3;, use this method:

  1. Read the current values on the right: here the old x.
  2. Calculate the right-hand expression: old x plus 3.
  3. Store that result in the variable on the left: x.
  4. Use the new state for the next statement.

The two appearances of x have different jobs: the one on the right supplies a value, while the one on the left names the destination. = is an instruction to store a value. It does not assert a mathematical equation that must remain true forever. With x currently 6, x = x + 3; is a valid update to 9; it is not the impossible equation “6 equals 9”. This reading rule applies to the simple assignments we use, not to every possible expression with side effects.

Worked example 1: use the current state

Return to the opening program. Keep both variable values visible, including the one that did not change.

  1. After int x = 6;: x=6; y has not been declared yet.
  2. After int y = 4;: x=6, y=4.
  3. For x = x + 3;, calculate 6 + 3 = 9. After the statement: x=9, y=4.
  4. For y = x - y;, calculate 9 - 4 = 5. After the statement: x=9, y=5.
  5. printf reads x=9 and y=5. It prints 9 5 followed by a newline. The variables remain x=9, y=5.

The second assignment sees the updated x=9. Using the starting x=6 again would incorrectly produce y=2. Each statement begins with the state left by the preceding statement; the assignments are not simultaneous.

A copy is not a permanent connection

A declaration can take its starting value from an existing variable. In the following body, copy receives the value that source has at that moment. It does not become a live formula for source.

All shorter code blocks in this lesson belong inside the same main body, with #include <stdio.h> above it and return 0; at the end. Each block is a separate example, starting afresh; do not concatenate the blocks.

C
int source = 6;
int copy = source;
source = 10;
printf("%d %d\n", source, copy);

After the declarations: source=6, copy=6. After source = 10;: source=10, copy=6. The output is 10 6. Assigning a new value to source does not also write to copy. A trace records stored numbers, not continuing algebraic relationships.

Arithmetic expressions and grouping

An expression describes a value to calculate. For the integer expressions in this lesson:

  • + adds, - subtracts, and * multiplies
  • / gives an integer quotient; % gives the corresponding remainder
  • Parentheses explicitly group a subexpression
  • *, /, and % bind more tightly than + and -

Binding means which operands belong to each operator. It is not a general promise about the timing of operand evaluation. Our arithmetic examples read values without updating them inside the expression, so their grouping determines a unique result.

C
int plain = 2 + 3 * 4;
int grouped = (2 + 3) * 4;
printf("%d %d\n", plain, grouped);

For plain, group the expression as 2 + (3 * 4): 3 * 4 is 12, then 2 + 12 is 14. For grouped, the parentheses require (2 + 3) to supply 5, then 5 * 4 is 20. The output is 14 20. C does not calculate every arithmetic expression by simply moving from the leftmost character to the right.

Whole groups and leftovers

If 23 items go into groups of 5, four full groups use 20 items and 3 remain. Thus 23 / 5 is 4 and 23 % 5 is 3. The quotient is an integer, not 4.6 rounded to the nearest integer. With a nonnegative total and a positive group size, the remainder is at least zero and smaller than the group size. A zero remainder means the division is exact.

The symbols have different roles: % between two integer expressions is remainder; %d inside a printf format is a request to print an integer. Neither means “percent” in these examples. Never divide by zero or take remainder with zero as the divisor. Negative operands are taught in the next lesson; do not extend a “leftover must be positive” shortcut to them.

Worked example 2: reconstruct the total

C
int total = 29;
int batch = 6;
int q = total / batch;
int r = total % batch;
int rebuilt = q * batch + r;
printf("%d %d %d\n", q, r, rebuilt);
  1. After int total = 29;: total=29.
  2. After int batch = 6;: total=29, batch=6.
  3. q = total / batch uses 29 / 6. Four complete batches fit, since 4 * 6 = 24, while 5 * 6 = 30 is too large. Now q=4.
  4. r = total % batch is the amount left after those four batches: 29 - 24 = 5. Now r=5; total, batch, and q have not changed.
  5. rebuilt = q * batch + r groups as (q * batch) + r: 4 * 6 + 5 = 29. Now rebuilt=29.
  6. The output is 4 5 29 followed by a newline, in the argument order q, r, rebuilt.

The check q * batch + r = total explains how the two results fit together. A claimed quotient and remainder must both satisfy this relation and, for these positive operands, leave less than one full batch. Merely choosing two numbers whose sum is 29 is not enough.

Practice: try all four before opening the solutions

These are original learning exercises. They are unscored; write intermediate states as well as an answer.

Practice question 1: two successive assignments

Trace both assignments and give the final a and b. What line is printed?

C
int a=8, b=5;
a=a-b;
b=b+a;
printf("%d %d\n",a,b);

Practice question 2: quotient and remainder

Find the whole groups and leftover items. Give the printed pair and check that it reconstructs the total.

C
int total=47, size=8;
printf("%d %d\n",total/size,total%size);

Practice question 3: arithmetic grouping

Evaluate the expression using C's grouping rules. Write the grouping that justifies your answer.

C
int value=7+2*5-3;
printf("%d\n",value);

Practice question 4: an attempted swap

Does this exchange the starting values of a and b? Give the final pair and explain where the original value of a goes.

C
int a=4,b=9;
a=b;
b=a;
printf("%d %d\n",a,b);

Practice solutions and wrong-turn feedback

Practice question 1 solution

  1. Initially a=8, b=5.
  2. a=a-b calculates 8-5=3; now a=3, b=5.
  3. b=b+a calculates 5+3=8; now a=3, b=8.
  4. Output: 3 8 followed by a newline.

If you got 3 13, you reused the starting a=8 in the last addition. Cross out that obsolete state after the first assignment. If you got 8 13, you treated the first assignment as if it did not store its result. Both statements change the named destination.

Practice question 2 solution

  1. Five full groups of 8 use 40 items; a sixth would require 48, more than 47.
  2. The quotient is 5; the remainder is 47-40=7.
  3. Output: 5 7 followed by a newline. Check: 5*8+7=47, with 0 <= 7 and 7 < 8.

6 -1 is wrong because integer division does not round 47/8 up. 5 0 discards the leftover items. 5.875 0 uses a fractional quotient, whereas both operands here are integers and / produces an integer result.

Practice question 3 solution

  1. Multiplication binds more tightly: group as 7 + (2 * 5) - 3.
  2. The product is 10.
  3. The remaining addition and subtraction group from the left: (7 + 10) - 3 = 14.
  4. Output: 14 followed by a newline.

42 comes from incorrectly grouping the expression as (7+2)*5-3. 11 would come from changing it to 7+2*(5-3). Neither grouping is written in the question. Insert parentheses to reveal the actual grouping, not to invent a different one.

Practice question 4 solution

  1. Initially a=4, b=9.
  2. a=b copies 9 into a; now a=9, b=9. No variable in this program keeps the old a=4.
  3. b=a reads the current a=9 and writes 9 to b; the pair remains 9, 9.
  4. Output: 9 9 followed by a newline. This did not swap the values.

9 4 assumes simultaneous exchange. 4 9 assumes neither assignment takes effect. A simple repair saves the value that would otherwise be overwritten:

C
int a = 4;
int b = 9;
int saved = a;
a = b;
b = saved;
printf("%d %d\n", a, b);

Here saved=4 before a changes. After a=b, a=9, b=9, saved=4. After b=saved, a=9, b=4, saved=4, and the output is 9 4. The repair works because the old number was explicitly stored, not because C remembers every overwritten value.

Before moving on

You can now trace a current state, distinguish initialization from later assignment, and group small integer calculations. Next, we will separate an expression's returned value from a change it makes to a variable, and extend division to negative integers.

Source notes

The code and traces are original. The language reference is the freely available WG14 N1570 C11 committee draft: entry point §5.1.2.2.1p1; minimum integer range §5.2.4.2.1; initialization §6.7.9p11; simple assignment §6.5.16.1p2; grouping §6.5.5p1 and §6.5.6p1; integer division/remainder §6.5.5p5–6; printf §7.21.6.3 and %d §7.21.6.1p8. These are semantic references, not a requirement to read a standards document before learning.

Analogy

Imagine two labelled erasable cards. The x card says 6 and the y card says 4. The instruction x = x + 3 means “read 6, calculate 9, erase the x card and write 9.” The instruction y = x - y then reads the cards as they are now: 9 and 4. It writes 5 on y. Copying a number onto a second card does not tie the cards together. If you need an old number after erasing it, first put it on a third card. The cards represent stored values; this analogy makes no claim about how physical memory is laid out.

Quick reference

  • int x = 6; declares and initializes x; x = 9; updates the existing x
  • State means current values. Record it after each statement
  • In a simple assignment, calculate the right-hand value from the current state, then store it in the left-hand variable
  • A copied value does not keep following the source variable
  • Parentheses set grouping; * / % bind more tightly than + -
  • For nonnegative a and positive b: a / b is the number of full groups; a % b is the leftover
  • Check: (a / b) * b + a % b equals a; the remainder is below b
  • Match each %d in printf with one int argument; \n ends the line
  • C11 convention: initialized int values, nonzero evaluated divisors, every intermediate within -32767 to 32767

Notes for this lesson

Sign in to keep your progress. Sign in