A strong answer
A strong candidate frames How do you find a wrong value with a debugger? around the exact memory layout, compiler semantics, and hardware execution model covered in Stepping Through a Program With a Debugger. Explain breakpoints, stepping choices, and variable inspection for locating the first line that produces a bad value.
It prints 7, and the answer should be 7.5. You could add std::cout lines, rebuild and rerun, but each experiment costs an edit-compile-run cycle, and you only see what you thought to print in advance. A debugger works the other way around. It pauses the running program, lets you read any variable, and walks forward one line at a time, so you watch what happens instead of guessing.
The -g flag records a table that maps machine-code addresses back to source lines, and memory locations back to variable names and types. This is the table lesson 3 meant when it said that names survive only as debug information. The -O0 flag turns the optimizer off, so the machine code stays in step with the source.
In a production interview, always connect the specification rule to its concrete runtime consequence: state what lives on the stack versus the heap or static storage segment, how the compiler or operating system validates alignments, lifetimes, and error codes, and what happens under adversarial or boundary inputs (such as zero-length buffers, integer overflow, concurrent mutation, or partial I/O). Backing your explanation with a small trace of register/memory state transitions immediately separates senior systems engineers from candidates who only memorized syntax.
What the interviewer is listening for
- Mastery of core invariant: Build a program so that a debugger can show source lines and variable names.
- Mastery of core invariant: Choose between step over, step into, step out and continue for a given situation.
- Mastery of core invariant: Explain how a breakpoint stops a running program.
- Hardware and ABI awareness: Explaining how sizes, alignments, calling conventions, or kernel boundaries behave on real 64-bit targets rather than relying on implementation-defined assumptions.
- Defensive engineering: Naming the exact compiler flags (
-Wall -Wextra -Werror), static analyzers, or runtime sanitizers (-fsanitize=address,undefined,thread) that verify correctness in CI.
Common mistakes
- Explaining only surface syntax without tracing the underlying memory addresses, ownership lifetime, or kernel/user-space state transitions.
- Assuming undefined behavior (such as signed overflow, uninitialized reads, strict-aliasing violations, or dangling pointers) will "just work" because it passed an unoptimized
-O0debug build. - Ignoring return codes, partial reads/writes, alignment padding, or cleanup paths when an allocation or syscall fails midway through a function.