Operators & Control Flow
Chapter 2 showed the standard being deliberately vague about integer sizes. This chapter shows the same looseness applied to something even more fundamental — what counts as "true."
Arithmetic, Comparison & Logical Operators
The familiar set — + - * / %, == != < > <= >=, && || ! — behaves mostly as expected, with one early trap: dividing two integers truncates, it doesn't round.
No True Boolean Before C99
This is the real subject of the chapter. Original C had no boolean type at all. Truth was just an int: 0 means false, any nonzero value means true — including negative numbers. Every if, every while, every logical expression in classic C evaluates to a plain integer, not a distinct boolean value the way Rust's bool always has been.
<stdbool.h> in Modern C
C99 added bool, true, and false — but even now, they're not a genuinely new primitive the way Rust's bool is. Under the hood, bool is a macro for _Bool (an integer type that can only hold 0 or 1), and true/false are just 1 and 0 in disguise.
if / else
Because truth is just "nonzero," C allows any expression as a condition — not just a genuine comparison.
This flexibility hides a classic, still-common bug: writing = (assignment) where == (comparison) was meant. if (x = 5) compiles cleanly, assigns 5 to x, and the condition is then "is 5 nonzero" — always true.
switch
Cases fall through by default — execution continues into the next case unless an explicit break stops it. Rust's match arms never fall through; each one is fully self-contained.
| Concept | Rust | C |
|---|---|---|
| Boolean type | bool — a real primitive, always existed | _Bool/bool since C99 — really an int in disguise |
| Condition requirement | must be a genuine bool | any nonzero expression works |
| match/switch fallthrough | never — each arm is isolated | falls through by default without break |
= vs == mistake is common enough that -Wall flags it directly. Compiling with warnings enabled from day one catches a real class of bugs the language itself won't stop you from writing.
match, C's switch silently falls through into the next case unless every case ends in break — a missing break doesn't warn by default and can execute code that was never meant to run for that case.
Coding Challenges
Write a program computing 7 / 2 as an int and 7.0 / 2 as a double, printing both. Explain the difference in the output.
📄 View solutionWrite a switch statement over an int day (1-3) that prints a different message for each day, deliberately omitting one break to demonstrate fallthrough, then fix it.
📄 View solutionExplain why if (x = 5) compiles without error in C, what it actually does, and why the equivalent mistake isn't possible in Rust.
📄 View solutionChapter 3 Quick Reference
- Integer division truncates —
5 / 2is2, not2.5 - Classic C has no boolean type —
0is false, any nonzero value is true <stdbool.h>(C99+) —bool/true/false, but really_Bool(an int) underneath- Any nonzero expression is a valid
ifcondition — not just genuine comparisons = vs ==in a condition compiles silently — enable-Wallto catch itswitchfalls through by default — every case needs an explicitbreak- Next chapter: loops — for/while/do-while and break/continue