So you think you know C? (2016)
A popular online C quiz that hinges on undefined and implementation‑defined behavior has reignited debate over what it means to “know C.” Commenters dissect edge cases involving integer sizes, struct padding, character encoding, shifts, and multiple increments, arguing over when answers are truly unknowable without specifying platform, compiler, and standard version. Many see such puzzles as pedantic or misleading compared to real‑world concerns like portability, testing on target systems, and avoiding unreadable code, while others defend them as valuable reminders of how fragile low‑level assumptions can be.
Overall reaction to the quiz
- Many see the quiz as a “gotcha” exercise about C’s corner cases and undefined behavior rather than a practical skills test.
- Some enjoyed it as a reminder of C’s subtleties; others found it pedantic, annoying, or a “waste of time” once they realized the metagame was “the correct answer is: you can’t know.”
- A recurring complaint: the answer option “I don’t know” is ambiguous; people wanted explicit choices like “undefined,” “unspecified,” or “implementation-defined.”
Undefined / implementation-defined / unspecified behavior
- Multiple comments dissect whether specific questions involve undefined behavior, implementation-defined behavior, or unspecified behavior.
- There is direct disagreement over the first question: some call it undefined, others implementation-defined, others “merely unspecified,” and some point out the article’s explanation itself assumes
sizeof(int) == 4, which isn’t guaranteed. - The struct layout, integer widths,
charsignedness, character encoding (ASCII vs EBCDIC), and shifts by ≥ bit-width are all cited as examples where the standard deliberately leaves behavior non-portable.
Concrete technical discussions
- Integer promotions: smaller-than-
inttypes are promoted tointbefore arithmetic, leading to surprising UB locations (e.g., 16-bit vs 32-bit multiply examples). - Shifts: shifting by ≥ width of the type is UB in C and C++; different CPUs handle it differently (masking, zero, trap).
- Multi-increment expressions (
i++ + ++i, chained XOR swap) are highlighted as classic undefined behavior related to sequence points. - Infinite loops: C and C++ differ in when compilers may optimize away loops with no observable side effects; this matters for embedded code.
- Union-based type punning is valid in C but not guaranteed in C++.
Practicality vs purity
- One camp argues that in real projects you target known platforms, rely on de facto norms (e.g., 32-bit
int, ASCII, IEEE-754), and back this with tests; strict standard conformance on every edge case is overkill. - Another camp stresses that relying on non-guaranteed behavior can break with new compilers, flags, architectures, or embedded targets; quizzes like this highlight dangerous assumptions.
Languages, tools, and alternatives
- Discussion branches into C++ quirks, D’s casting syntax and integer sizing, Rust’s safer shift methods, Scheme via Gambit-C, and GC use in C.
- Several argue there are better educational resources: best practices, memory management, safer idioms, and books (including the linked PDF from the same site).