Layered defense, because UB is invisible by default, the compiler isn't required to warn. First, compile with -Wall -Wextra (and ideally -Werror) to catch precursors: uninitialized variable use, signed/unsigned comparison mismatches, suspicious shifts, missing returns. Second, run the sanitizers during development and testing: UndefinedBehaviorSanitizer (-fsanitize=undefined) instruments the binary to trap signed overflow, out-of-bounds shifts, misaligned accesses, and null derefs with exact file/line diagnostics; AddressSanitizer (-fsanitize=address) catches out-of-bounds memory access, use-after-free, and leaks. Third, use static analyzers (clang-tidy, Coverity, cppcheck) which flag some UB by analysis without running. For embedded specifically, you run the sanitizers against host-side unit tests of your portable logic, since instrumented binaries are usually too big and slow for the target, which is itself an argument for keeping firmware logic decoupled from hardware so it's testable on a PC. The overarching discipline: never treat "it works" as proof, and prefer safe idioms (unsigned for wraparound, 1u << n, memcpy for punning, bounds checks at input boundaries).
C Programming · Interview question
How do you find undefined behavior in a codebase?
A strong answer
What a weak answer sounds like
You know the answer. Do you know what gets you dinged?
Pro breaks down the answer most candidates actually give to this question — and the specific reason an interviewer marks it down. It’s the difference between sounding correct and sounding senior, on all 472 questions.
From the lesson
Undefined Behavior & Pitfalls
Why code that works at -O0 breaks at -O2: what undefined behavior is, the catalog of UB every firmware engineer must recognize, and how sanitizers catch it.