C++ Module 21
Embedded C++ interview questions
31 interview questions on embedded C++ from C++: short warm-ups, then what each harder question tests, the first step of a good answer and its follow-ups.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher to mid
What is the difference between a hosted and a freestanding C++ implementation, and which one does Cortex-M firmware use?
Answered in the lesson Embedded C++: freestanding, no exceptions, no heap
-
Fresher to mid
What do the three parts of arm-none-eabi mean, and why can the program it builds not run on the laptop that built it?
Answered in the lesson Cross-compiling for Arm Cortex-M and running in QEMU
-
Fresher to mid
What does volatile guarantee for an access to a memory-mapped register, and what does it not guarantee?
Answered in the lesson Memory-mapped registers and volatile in C++
-
Fresher to mid
What should an interrupt handler avoid doing, and why?
Answered in the lesson Interrupt-safe ring buffers in embedded C++
-
Fresher to mid
What do Q15 and Q16.16 mean, and what range and step does each give?
Answered in the lesson Fixed-point arithmetic in C++ firmware
-
Fresher to mid
Why should a firmware state machine handle every event in every state, even the events it ignores?
-
Fresher to mid
What is a hardware abstraction layer, and why does it make firmware testable on a laptop?
Answered in the lesson Testing firmware logic on your laptop
-
Fresher to mid
What does a coding guideline set such as MISRA C++:2023 try to prevent, and why are tests alone not enough?
Answered in the lesson Safety-critical C++ and MISRA C++:2023
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
A Cortex-M4 firmware must fit in 256 KB of flash. The first build, which parses its configuration with std::stoi, std::vector and exceptions and logs through iostream, fails to link because the flash region overflows. How do you find what is taking the space, and what do you change?
- What it tests
- Reading linker errors, size reports and symbol sizes, the costs of iostream, exceptions and their terminate handler, and replacing them with fixed-capacity code that reports errors as values.
- First step
- Do not guess. Link once against a temporarily larger region, or read the linker map, and list the largest symbols with nm --size-sort. Then group them by what pulled them in: iostream's locales and streams, the exception runtime and its terminate handler, printf's formatting engine, and the heap.
- Follow-ups
-
- How much does turning off exceptions save if one library call can still throw?
- Does linking newlib-nano save space at no cost?
- How do you stop the problem from coming back?
From the lesson Embedded C++: freestanding, no exceptions, no heap
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Your firmware is built with -fno-exceptions. How should a sensor driver report that the sensor did not answer, and what happens to standard library calls that would normally throw, such as std::vector::at or std::optional::value?
- What it tests
- Error handling without exceptions with std::expected, std::optional and error codes, what libstdc++ does at throw sites, and C++26's freestanding-deleted functions.
- First step
- Separate expected failures from bugs. A sensor that does not answer is an expected failure, so it belongs in the return type, for example std::expected<Reading, SensorError>. A broken invariant is a bug, which should stop the program in a defined way rather than travel up as a value.
- Follow-ups
-
- Why not keep exceptions enabled and simply never throw?
- What does std::vector::at do in such a build?
- How does C++26 help firmware here?
From the lesson Embedded C++: freestanding, no exceptions, no heap
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A global int threshold = 25; reads as 0 on the board, while a host build of the same code prints 25. The firmware has its own linker script and start-up code. What do you check, and in what order?
- What it tests
- Load and run addresses of .data, copying .data and zeroing .bss in start-up code, the linker-script symbols they use, and reading the map file or ELF headers.
- First step
- Ask where the 25 is before main runs, and what puts it into RAM: on the board nothing does unless the start-up code does. Check that code, and the linker-script symbols it relies on, before you suspect the C++.
- Follow-ups
-
- How do you confirm it from the ELF file without a debugger?
- Why is a zero-initialised global fine while the initialised one is wrong?
- Where do constructors of global objects fit in?
From the lesson Cross-compiling for Arm Cortex-M and running in QEMU
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Your team wants to run firmware tests in QEMU on the build server instead of on boards. What can QEMU tell you reliably, what can it not, and how would you design the test setup?
- What it tests
- What emulation models and does not model, semihosting for test output and exit codes, determinism with icount, and a test pyramid across host, emulator and hardware.
- First step
- Sort the risks before choosing tools: list what can go wrong with the firmware, from its logic and its start-up code to timing, peripherals and the chip's errata, and for each ask whether an emulator models it at all. Then decide what each layer proves and how a test reports its result from inside QEMU.
- Follow-ups
-
- How does a test report pass or fail from inside QEMU?
- Why might a timing-dependent test pass in QEMU and fail on the board?
- How do you make QEMU runs reproducible?
From the lesson Cross-compiling for Arm Cortex-M and running in QEMU
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Debug builds at -O0 no longer fit in flash, and one module behaves differently between the debug build and the -Os release build. How do you approach the two problems?
- What it tests
- Size differences between optimisation levels, -Og, section garbage collection, and why optimisation exposes undefined behaviour, missing volatile or races with interrupts.
- First step
- Treat them separately. For size, measure before you change a flag: compare the two size reports and the largest symbols. For the behaviour difference, suspect code whose meaning depends on the optimiser: undefined behaviour, a register accessed without volatile, or data shared with an interrupt without atomics.
- Follow-ups
-
- What do -ffunction-sections and --gc-sections remove?
- Is shipping -O0 firmware a reasonable way out?
- How do you find the undefined behaviour?
From the lesson Cross-compiling for Arm Cortex-M and running in QEMU
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A loop that polls a status register works at -O0 and hangs at -O2 on the board. The register is read through a plain std::uint32_t pointer. Explain what the optimiser did, how you would prove it, and how you fix it.
- What it tests
- The as-if rule and observable behaviour, why plain loads may be hoisted out of a loop, reading disassembly, and the correct use of volatile for registers.
- First step
- Ask what the compiler may assume about a plain object: nothing in the loop writes it, so its value cannot change, and one load before the loop, or none, is as good as one per pass. Then confirm it in the disassembly of the loop rather than by trying flags.
- Follow-ups
-
- Why did it work at -O0?
- Would making the pointer const or adding a compiler barrier fix it?
- Is std::atomic a better fix than volatile here?
From the lesson Memory-mapped registers and volatile in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A driver maps a peripheral's control register as a struct of volatile bit-fields and sets several fields one after another. What can go wrong, on Arm and elsewhere, and what do you propose instead?
- What it tests
- Implementation-defined bit-field layout, the AAPCS rules for volatile bit-field accesses, hidden reads of registers with side effects, write-one-to-clear flags, and alternatives with masks or typed fields.
- First step
- Count the bus accesses each line of the driver makes, and check them against what the target's ABI says a write to a volatile bit-field does. Then look for registers where a read, or writing back what was read, has a side effect the peripheral notices.
- Follow-ups
-
- When is a bit-field struct acceptable?
- What happens on a register whose flags clear when 1 is written?
- How would you migrate the drivers without rewriting everything at once?
From the lesson Memory-mapped registers and volatile in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Design a type-safe register access layer in C++ for a team's drivers: fields with access rights, combined writes, read-modify-write, and tests on the host. What does it cost at run time, and how do you show that?
- What it tests
- Templates and constraints for register descriptions, compile-time rejection of misuse, zero-cost abstraction checked in the disassembly, a bus abstraction for host tests, and generation from vendor descriptions.
- First step
- Start from what drivers do with registers: read one field, set several fields at once, change one field and keep the others, and what each of these must never do to the hardware. Then decide how to describe registers and fields so that the compiler checks those rules, and how a test can stand in for the hardware.
- Follow-ups
-
- How do you prove the layer costs nothing?
- Which mistakes should fail to compile?
- Where do the register descriptions come from?
From the lesson Memory-mapped registers and volatile in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
An interrupt handler and the main loop both increment a shared volatile counter, and at the end of a test the total is lower than the number of increments. Explain why, estimate how many are lost, and give two fixes with their costs.
- What it tests
- Read-modify-write races with an interrupt, why volatile does not make them atomic, estimating losses from the instruction window, and critical sections against atomic operations.
- First step
- Write out main's increment as instructions: a load, an add and a store, with the loop's own instructions around them. An interrupt that arrives between the load and the store updates the counter, then main stores a value computed from the old one. The share of instruction boundaries inside that window predicts the share lost.
- Follow-ups
-
- Does the handler's own increment need protecting too?
- Which fix would you choose on a Cortex-M0+?
- How would you design the code so the problem cannot appear?
From the lesson Interrupt-safe ring buffers in embedded C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Design the buffer between a UART receive interrupt and the main loop. Bytes arrive at 115,200 baud, and the main loop sometimes stalls for 20 ms while it writes to flash. What size, which indices and memory orders, and what happens when it is full?
- What it tests
- Sizing from the arrival rate and the longest stall, single-producer single-consumer design with release and acquire, power-of-two capacity, the full policy, and how to test it.
- First step
- Work out the arrival rate: with 8 data bits, no parity and one stop bit, each byte takes 10 bit times, so 115,200 baud delivers 11,520 bytes a second. A 20 ms stall therefore lets about 230 bytes arrive, which sets the minimum capacity before any margin.
- Follow-ups
-
- Why a power of two, and is 256 enough?
- What should the handler do when the ring is full?
- How do you test the ring before the board exists?
From the lesson Interrupt-safe ring buffers in embedded C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Code that calls fetch_add on a std::atomic<std::uint32_t> compiles and links for a Cortex-M4 but fails to link for a Cortex-M0+ with an undefined __atomic_fetch_add_4. Why, and what are your options?
- What it tests
- Instruction-set support for atomic read-modify-write, is_always_lock_free, compiler library calls for atomics, critical sections on a single core, and designs that need no read-modify-write.
- First step
- Start from the missing symbol: __atomic_fetch_add_4 is a function the compiler calls when it cannot build the operation from instructions. Compare what the Cortex-M0+ and the Cortex-M4 offer for an atomic read-modify-write, and what std::atomic<std::uint32_t>::is_always_lock_free says on each.
- Follow-ups
-
- Is it safe to provide __atomic_fetch_add_4 yourself?
- Which atomics still work on the Cortex-M0+?
- How would you write the code so it does not care?
From the lesson Interrupt-safe ring buffers in embedded C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A Q16.16 low-pass filter, y += alpha * (x - y) with alpha = 0.01, runs on a Cortex-M0. With a constant input of 2.5 it settles 100 steps of 1/65,536 below 2.5 and never gets closer. Explain why, and fix it.
- What it tests
- Fixed-point rounding, the dead band of recursive filters, and fixes that keep precision without a floating-point unit or expensive arithmetic.
- First step
- Write the update in raw integers: the increment is alpha's raw value times the difference x − y, shifted right by 16. Find the largest difference for which that increment is zero; once the output is that close, it cannot move.
- Follow-ups
-
- Does rounding to nearest fix it?
- How does error feedback work, and what does it cost on a Cortex-M0?
- How would you test for this before the device ships?
From the lesson Fixed-point arithmetic in C++ firmware
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
You are porting a PID control loop from float on a PC to a Cortex-M0 without a floating-point unit. How do you choose a fixed-point format for each variable, and how do you show that the port is accurate enough?
- What it tests
- Range and resolution analysis, headroom for products and sums, per-variable scaling, the cost of operations on a small core, and verifying a port against a floating-point reference.
- First step
- List every variable with its physical range and the smallest change that matters, taken from the sensor, actuator and control specifications. The range sets the integer bits, the resolution sets the fraction bits, and every product and sum needs its own headroom.
- Follow-ups
-
- Where does the integral term overflow first, and what do you do about it?
- Which operations become library calls on a Cortex-M0?
- What tolerance do you test against, and why not equality?
From the lesson Fixed-point arithmetic in C++ firmware
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A golden-output test compares 1,000 outputs of a float filter bit for bit. It passes on developers' machines and fails on the CI server, which builds the same code with GCC at -O2 for the same processor. What is the likely cause, and what do you change?
- What it tests
- Floating-point contraction and other build-dependent rounding, deterministic testing strategies, and when fixed point is the right fix rather than a looser test.
- First step
- Compare the two builds' compiler and flags before suspecting the code. The optimisation level and the contraction setting decide whether a multiplication and an addition become one fused instruction, which changes the last bit of some results.
- Follow-ups
-
- How do you confirm that contraction is the cause?
- Tolerance, compiler flags or fixed point, which do you choose here?
- What else can make float results differ between builds?
From the lesson Fixed-point arithmetic in C++ firmware
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
An interrupt handler calls the door controller's dispatch() when the motor reaches its end stop, and the main loop calls dispatch() for keypad events. About once a week a unit ends up in the wrong state. What do you suspect, and how do you restructure the code?
- What it tests
- Re-entrancy and run-to-completion, data races between an interrupt and the main loop, event queues, and making a rare failure reproducible.
- First step
- Notice that dispatch() has two callers that can interrupt each other. If the interrupt arrives while the main loop is halfway through a transition, the inner call sees a half-updated state, and the outer call then overwrites what the inner call did.
- Follow-ups
-
- How would you reproduce a failure that happens once a week?
- Why not simply disable interrupts inside dispatch()?
- What must the event queue guarantee between an interrupt and the main loop?
From the lesson State machines for firmware in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A battery charger controller has 12 states and 15 events, and two states must remember data: a charge timer and a fault code. Your team argues between a switch, a transition table and std::variant on a Cortex-M0+ with 32 KB of flash. How do you decide?
- What it tests
- Weighing reviewability, compile-time checks, data that belongs to one state and code size, measuring instead of assuming, and keeping the tests independent of the form chosen.
- First step
- Write down what the code must make hard to get wrong: forgotten events, conflicting transitions, data used in the wrong state. Then measure each candidate's flash cost with the real compiler and flags, because on a small part the cheapest form to read is not always the cheapest to store.
- Follow-ups
-
- How do you keep the decision reversible?
- Where does the two states' data live in each form?
- Would you generate the code from a table instead?
From the lesson State machines for firmware in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
How do you test a state machine with 8 states and 10 events so that you know every transition is covered, and what can such tests still miss?
- What it tests
- Exhaustive testing of state and event pairs, reaching states deliberately, specification tables as the oracle, random walks with invariants, and the limits of tests that feed events by hand.
- First step
- Count the pairs: 8 states times 10 events are 80 cells, each with an expected next state from the specification. A test that drives the machine into every state and sends every event checks all 80 directly.
- Follow-ups
-
- How do you reach every state without depending on other transitions being right?
- What does a long random walk add to the 80 cells?
- Which failures need timing or a second caller before they appear?
From the lesson State machines for firmware in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Your driver's unit tests pass on every developer's laptop, but on the device the same code reports −40 °C as 216 °C. What could make the host and the target disagree, and how do you catch such bugs before flashing?
- What it tests
- Knowing the ABI differences between a computer and a microcontroller, reasoning from a symptom to a type, and running tests under the target's rules.
- First step
- Look at the numbers: 216 is 256 − 40, so a byte that should be negative is read as unsigned. Find where the driver turns a register byte into a signed value, and ask whether that step depends on how plain char is defined.
- Follow-ups
-
- How would you reproduce it without the device?
- Which other differences between host and target cause bugs of this kind?
- Which of them can a static check find?
From the lesson Testing firmware logic on your laptop
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A team writes 40 drivers for a Cortex-M0+ with 32 KB of flash and wants every driver testable on the host. Should the hardware access be injected with templates or with virtual interfaces?
- What it tests
- Measuring the cost of indirection and inlining on a small core, code duplication from templates, build structure, and the kinds of test double each choice allows.
- First step
- Measure before deciding: build one driver both ways with the real compiler and flags, compare the size and the calls in the generated code, then scale by 40 drivers and by the number of bus types each driver must work with.
- Follow-ups
-
- What if two drivers need two different buses in one firmware?
- How do concepts change the trade-off?
- Where would you still use a virtual interface?
From the lesson Testing firmware logic on your laptop
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Which parts of a firmware can host tests not be trusted with, and what does your test strategy look like from a commit to a release?
- What it tests
- Knowing what host tests and emulators can and cannot show, layering tests by cost, and turning hardware-only risks into targeted tests on real boards.
- First step
- List the risks by where they live: logic and decoding, which host tests cover; types, the ABI and the compiler's code, which an emulator covers; and timing, interrupts and peripherals, which only real hardware shows.
- Follow-ups
-
- What runs on every commit, and what before a release?
- How do you test interrupt timing without real hardware?
- How do you keep the hardware tests from decaying?
From the lesson Testing firmware logic on your laptop
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Your product must follow MISRA C++:2023, but a third-party communication library you need uses exceptions, dynamic memory and recursion. What do you do?
- What it tests
- Handling adopted code under a guideline set: isolation, deviations for adopted code, wrappers, evidence, and the trade-off against replacing the library.
- First step
- Treat it as adopted code, not as your own: MISRA Compliance expects adopted code to be identified and its violations justified on that basis. Then decide where the library may run and how its failures are contained.
- Follow-ups
-
- How do you contain its dynamic memory and exceptions?
- What evidence would you give an assessor?
- When would you replace the library instead?
From the lesson Safety-critical C++ and MISRA C++:2023
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
An analyser reports 3,000 guideline findings on an existing 80,000-line code base that must now comply. How do you plan the work?
- What it tests
- Triage by category and risk, baselining, gates on new code, re-categorisation, deviations, and measuring progress without gaming the count.
- First step
- Do not start fixing in file order. Freeze a baseline, stop new violations with a gate on changed lines, then sort the 3,000 by guideline category and by what the code does, so that mandatory rules and safety-relevant modules come first.
- Follow-ups
-
- Which findings would you never deviate, only fix?
- How do you stop the count from being gamed?
- How do you know you are done?
From the lesson Safety-critical C++ and MISRA C++:2023
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Does adopting C++26 contracts and a hardened standard library make a guideline set such as MISRA C++:2023 unnecessary?
- What it tests
- Run-time checks against prevention, evaluation semantics, what certification needs as evidence, and how the two approaches combine.
- First step
- Separate when each acts: contracts and hardening check conditions while the program runs and stop or report a violation; guidelines and analysers aim to keep the violation from being written at all. A safety argument needs both.
- Follow-ups
-
- What does the evaluation semantic of a contract change here?
- Which guideline categories do contracts and hardening not touch?
- How would you use contracts in a MISRA project today?
From the lesson Safety-critical C++ and MISRA C++:2023
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes