Week 4: Increment, Decrement, and Bitwise Operators
Postfix against prefix for ++ and --, then all six bitwise operators on the same inputs with the binary table worked by hand: old value versus new value, the overflow guards, and why ~12 prints -13.
Operators With Side Effects
Week 4 is chapter 3's operator lesson turned into experiments. ++ and -- are the only arithmetic operators that change their operand, and whether they hand back the old value or the new one depends on which side of the variable they sit. The bitwise operators do not change anything, but they work on a number's bits rather than its value, and the lab asks you to compute all six of them on the same inputs and print the results. Each program is short; the marks are for predicting the output before running it, so try that with every input suggested below.
Program 1: The Increment Operator
Show the difference between postfix x++ and prefix ++x. The clean way is two experiments on the same starting value: assign y = x++ and print both, then reset and assign y = ++x and print both.
Algorithm:
- Start.
- Read n.
- Set x to n, then set y to x++ and print y and x.
- Set x back to n, then set y to ++x and print y and x.
- Stop.
With 5 the first experiment prints y = 5, x = 6 and the second prints y = 6, x = 6. Both forms leave x at 6; they differ only in what the expression is worth. Postfix yields the value x had before, prefix yields the value it has after. When the result is not used, as in a for loop's i++, the two are interchangeable, and this program is the proof that they are not interchangeable the moment the result is assigned.
The INT_MAX check is unusual in a lab program and worth a sentence in the record. Incrementing the largest int overflows a signed integer, and chapter 3 was blunt about that: it is undefined behaviour, not "wraps to negative". INT_MAX comes from <limits.h>, and refusing that one input is what makes every other line of the program defined.
Do not extend this experiment to y = x++ + ++x or x = x++. Chapter 3 named those as undefined behaviour, modifying x twice between sequence points, and a textbook answer key that gives them an output is wrong, whatever number it prints.
Program 2: The Decrement Operator
The same two experiments with --.
Algorithm:
- Start.
- Read n.
- Set x to n, then set y to x-- and print y and x.
- Set x back to n, then set y to --x and print y and x.
- Stop.
5 gives y = 5, x = 4 and then y = 4, x = 4. Try 0 as well: the postfix line reports y = 0, x = -1, a reminder that nothing stops a count going below zero unless the program checks. The guard is the mirror image of the last one, INT_MIN this time, because decrementing the most negative int overflows in the other direction.
Program 3: The Bitwise Operators
Read a, b, and a shift count n, then compute and print c = a & b, d = a | b, e = ~a, f = a >> n, g = a << n, and h = a ^ b.
Algorithm:
- Start.
- Read a, b, and n.
- If a or b is negative, or n is outside 0 to 15, or a << n would not fit in an int, print a message and stop.
- Compute the six results and print each with its expression.
- Stop.
Run it with 12 10 2 and check the first, second, and last lines against the bits by hand:
a = 12 = 1100
b = 10 = 1010
a & b = 8 = 1000 1 only where both are 1
a | b = 14 = 1110 1 where either is 1
a ^ b = 6 = 0110 1 where they differ
Writing that table in the lab record is the point of the program; the examiner wants to see that &, |, and ^ were worked column by column, not looked up.
The shifts are multiplication and division by powers of two: 12 >> 2 is 3 and 12 << 2 is 48. And ~12 prints -13, which surprises everyone the first time. ~ flips every one of the 32 bits of the int, including the sign bit, and in two's complement, the representation this machine uses, the flipped pattern is the number −(a + 1). C89 does not promise two's complement, so ~a on a signed value is implementation-defined; the relationship ~a == -a - 1 is what you will see on any machine you meet, and the phrase "implementation-defined" is the honest answer to why.
The three guards exist because the shift operators have the sharpest edges in the language. Shifting a negative value left is undefined behaviour, and shifting one right is implementation-defined, so a and b are held non-negative. Shifting by a negative count or by 32 or more, the width of an int here, is undefined, hence the range on n. And a << n is undefined if the result does not fit in an int, which is what the last check computes: a must be at most INT_MAX divided by 2 to the n. Try 100000 1 15 and watch that guard fire. The check for n stops at 15 rather than 31 for a reason from chapter 2: the standard only promises that an int has 16 bits, so a shift of 16 or more is already undefined on the smallest conforming machine, and the guard is written for the standard, not for this laptop.
Key Takeaways
- Postfix
x++yields the old value and prefix++xyields the new one; both leavexincremented, so they differ only when the result is used. - Incrementing
INT_MAXor decrementingINT_MINis signed overflow, which is undefined behaviour;<limits.h>gives you the names to check against. - Modifying a variable twice in one expression is undefined behaviour; no output is the correct output.
&,|, and^work column by column on the bits; show the binary table in the record.~aon this platform is-a - 1, a two's complement fact the standard calls implementation-defined.- Shifts: negative left operand undefined, negative or too-large count undefined, left-shift overflow undefined. Guard all three before shifting.
How did you find this lesson?
Your rating helps us improve the content.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Practice Exercises
The Increment Operator, Postfix and Prefix
Read an integer n and run two experiments that show the difference between postfix and prefix increment. Print start: x = 5 with the value of n. Then set x to n, assign y = x++, and print after y = x++: y = 5, x = 6. Then set x back to n, assign y = ++x, and print after y = ++x: y = 6, x = 6. Each assignment must use the increment operator itself, not + 1. If n cannot be read, print invalid input and return 1. If n is INT_MAX, incrementing it would overflow, so print n is too large to increment and return 1.
The Decrement Operator, Postfix and Prefix
Read an integer n and run two experiments that show the difference between postfix and prefix decrement. Print start: x = 5 with the value of n. Then set x to n, assign y = x--, and print after y = x--: y = 5, x = 4. Then set x back to n, assign y = --x, and print after y = --x: y = 4, x = 4. Each assignment must use the decrement operator itself, not - 1. If n cannot be read, print invalid input and return 1. If n is INT_MIN, decrementing it would overflow, so print n is too small to decrement and return 1.
The Six Bitwise Operators
Read three integers a, b, and n, then compute and print c = a & b, d = a | b, e = ~a, f = a >> n, g = a << n, and h = a ^ b, one per line in exactly this form: c = a & b = 8, d = a | b = 14, e = ~a = -13, f = a >> n = 3, g = a << n = 48, h = a ^ b = 6. Guard the shifts before computing anything: if a or b is negative print a and b must be non-negative; otherwise if n is below 0 or above 15 print n must be between 0 and 15; otherwise if a is greater than INT_MAX >> n, so that a << n would overflow, print a << n would overflow. Each of those returns 1 without printing any results. If the three integers cannot be read, print invalid input and return 1.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!