๐ WAYPOINT LESSON
Invariants: Assertions That Catch Bugs Early
โญโญโญ advancedโณ 16 min read๐ Lesson 196 of 225
The proven debugging method when no debugger exists: encode what must be true, and let the violation point at the culprit.
Debugging without symbols
Professional C work happens in environments where you cannot step through code: production, CI, embedded targets, sandboxes. The durable method is invariant-driven debugging: for each data structure and loop, write down the property that must always hold, then check it aggressively in debug builds.
static void ds_invariant(const int *heap, size_t n) {
for (size_t i = 1; i < n; i++) {
size_t parent = (i - 1) / 2;
if (heap[parent] < heap[i]) {
fprintf(stderr, "heap invariant broken at %zu\nn", i);
abort();
}
}
}
This is what -DNDEBUG toggles: with NDEBUG the compiler drops assert(); your own _invariant functions can stay compiled-in for tests and compiled-out for production.
The forensic sequence
- Reproduce โ a failing input you can paste, not a story.
- Localize โ bisect: which half of the program corrupts the state?
- Hypothesize โ name the invariant you believe is broken.
- Prove โ an assertion that fires earlier than the crash.
- Fix the cause, not the symptom โ then keep the assertion.
Crashes are late. Invariants are early. A corruption found at free() was caused lines earlier; the assertion lattice shrinks that distance to near zero.
โก Now practice
Ready to CodeForensics DrillsInvariant lattices, binary-search diagnosis, and repro discipline โ as testable functions.
4 challenges ยท ยท ~20 min