All jobs / Embedded / hardware

C / C++ interview questions

C / C++ questions a Embedded / hardware can be asked, from first job to senior. Open one to see the strongest answer and why it works.

  1. A status register never seems to change in your loop, but the scope says it does. First suspect?

    The strongest answer: The compiler cached the read — I mark MMIO volatile (or use the vendor accessor) so each access hits the hardware

    Why it works: Exactly. MMIO is not a RAM variable. Volatile or the accessor is the contract.

    • Treat MMIO as volatile.
    • Do not rely on printf to “refresh” a register.
    • Do not ban optimisation as the design.
    • The scope can be right when C looks stuck.
  2. Why might you mark a lookup table `const` on a microcontroller?

    The strongest answer: So it can live in flash, not eat RAM — and so I do not write it by accident

    Why it works: Exactly. RAM is the scarce one. const is a placement and a promise.

    • const tables can live in flash.
    • Saves RAM; blocks writes.
    • Not merely a linter toy.
    • Do not leave placement to vibes.
  3. What does `static` mean on a file-scope variable, on a local variable, and on a function?

    The strongest answer: At file scope it hides the symbol from other translation units; on a local it keeps the value between calls; on a function it stops the name being linked from anywhere else

    Why it works: Exactly — one keyword, two jobs: linkage at file scope, lifetime inside a function. Knowing which one applies saves a lot of confusion.

    • Static means lifetime or linkage, never immutability.
    • File scope: hidden from other files.
    • Local: value survives the call.
    • Static state is shared state — mind ISRs.
  4. You need bit 3 of a register. Strongest habit?

    The strongest answer: A named mask and a read-modify-write I can show — not a magic `|= 8` I cannot explain next month

    Why it works: Exactly. Names and RMW. Flooding a register is how you start a motor in a debug session.

    • Named masks + careful RMW.
    • Do not write all-ones.
    • Do not toggle to discover.
    • Do not guess the header.
  5. Dereferencing NULL on many MCUs does what?

    The strongest answer: Often a fault — or a wrap to address zero if you were sloppy — I check pointers and I do not treat NULL as a cute unused value

    Why it works: Exactly. No Java. Fault or trash at zero. Swallowing faults is a product defect.

    • NULL is a fault or a zero-page smash.
    • Check pointers.
    • Bare metal ≠ safe deref.
    • Do not swallow faults as 0.
  6. The team ignores 400 compiler warnings. Your line?

    The strongest answer: We turn on -Wall -Werror (or a written exception list) — a warning is a bug we have not read

    Why it works: Exactly. Read or except. Tree-wide pragmas are how you miss a real one.

    • Treat warnings as unread bugs.
    • Werror or a written exception.
    • No tree-wide hide.
    • Linker-clean ≠ safe.
  7. In C++ firmware, why would you prefer a `constexpr` constant over a `#define`?

    The strongest answer: constexpr has a type and obeys scope, so the compiler checks it and the debugger can show it; a macro is blind text substitution that ignores scope and leaves nothing to inspect

    Why it works: Right — a typed, scoped constant is one the compiler can check and the debugger can print. A macro does neither.

    • Prefer a typed constant to text substitution.
    • constexpr is type checked and scoped.
    • The debugger can show it; macros vanish.
    • If you must macro, parenthesise everything.
  8. A 32-bit MCU, a 64-bit counter shared with an ISR. What do you do?

    The strongest answer: Make the access atomic for that width, or disable the ISR around a consistent read — I do not assume a torn read is rare enough

    Why it works: Exactly. Width and tearing. Prints are not atomics. Floats are not a lock.

    • Protect width across ISR/thread.
    • 64-bit can tear on 32-bit.
    • Floats are not atomic magic.
    • Prints ≠ synchronisation.
  9. A struct is cast straight onto a UART receive buffer, and the fields come out shifted on one of the two boards.

    What is going on, and what would you do instead?

    The strongest answer: The compiler pads members up to their alignment and the two builds pad differently. I serialise field by field into a byte buffer with a fixed order instead of casting a struct

    Why it works: Exactly — a wire format is a byte layout you define, not whatever your compiler happened to lay out today.

    • Define the wire format; never cast a struct onto it.
    • Padding is per compiler and target.
    • Serialise field by field, fixed order.
    • Packed still leaves alignment and endianness.
  10. How do you use a coding standard without worship?

    The strongest answer: A written deviation with a reason, and I fix the ones that hide undefined behaviour — I do not #pragma the tree to “go green”

    Why it works: Exactly. Read, deviate on purpose. Tree pragmas and box badges are theatre.

    • Fix UB-hiding issues.
    • Written deviations.
    • No tree-wide pragma green.
    • No box badge as proof.
  11. When do you allow C++ on the MCU, and what do you ban?

    The strongest answer: A written subset: no throwing across the RTOS, no hidden heap in ctors I cannot see — I do not say “modern C++” as a plan

    Why it works: Exactly. A subset you can debug at 02:00. Desktop exceptions and macro objects are two cliffs.

    • Written C++ subset.
    • No hidden heap or throws across the kernel.
    • “Modern” is not a plan.
    • Do not fragment dialects per app.