Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++  Module 21 – Embedded C++

Safety-critical C++ and MISRA C++:2023

What coding guidelines for safety-critical C++ prevent, what MISRA C++:2023 covers and how it is licensed, and where analysers and C++26 checks fit in.

  • Advanced
  • 20 minutes
  • Examples run with Clang 21.0.0, GCC 16.2.0 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 7 of 20 minutes free
Free: Guidelines remove behaviour that no test can pin down · MISRA C++:2023 serves critical systems, the Core Guidelines everyday code · Three checkers, three different slices of one function's bugs · Practise. With a pass: 2 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Explain what coding guidelines for safety-critical systems try to prevent
  • Describe what MISRA C++:2023 covers and how it is licensed
  • Compare what compiler warnings, clang-tidy and the sanitizers each find in one function
  • Record a deviation and review a module against the guideline categories

Before you start

A short function that averages sensor readings goes through the compiler’s warnings, clang-tidy and the sanitizers in this lesson, and three of its bugs still go unreported, among them a division by zero. Safety-critical C++ is code whose failure can injure people, in a car’s braking controller, an infusion pump or a railway signal, so it is written to coding guidelines that keep the language to a subset whose behaviour is defined, predictable and checkable by tools: a wrong value cannot be taken back once it has done harm.

MISRA C++:2023 is the guideline set written for this work: MISRA’s guidelines for the use of C++17 in critical systems. It is sold as a document whose text is protected by copyright, so this lesson explains what its rules aim at without quoting any. The C++ Core Guidelines, open to anyone online, cover much of the same ground for everyday code. Tools find many violations automatically; where a rule must be broken, a written A written record that lets a project break one guideline in stated places, saying why, what the risk is and which precautions keep it under control. records why.

Guidelines remove behaviour that no test can pin down

Coding guidelines for safety-critical C++ remove whatever makes a program’s behaviour hard to predict or to prove, from undefined behaviour: What a program does when it breaks a rule for which the C++ standard sets no requirements, such as reading past the end of an array. The compiler may assume it never happens, so the result can change with the compiler and the optimisation level. More in “Object lifetime and undefined behaviour in C++” to implementation-defined behaviour: Behaviour that the C++ standard leaves to each implementation, which must document its choice, such as sizeof(int). A program that relies on it is valid, but may give other results with another compiler or platform. More in “Object lifetime and undefined behaviour in C++”. The same concerns recur across guideline sets:

Concern What it covers, and why it goes
Undefined behaviour Signed overflow, reading past an array, a dangling reference, an uninitialised read. The compiler may assume it never happens, so no test result can be trusted once it does
Behaviour the implementation chooses Whether plain char is signed and how big long is, which each compiler documents; the order in which function arguments are evaluated, which it need not. Code that relies on one choice breaks on another compiler or processor
Conversions that change a value Narrowing, sign changes, a floating-point value turned into an integer
Dynamic memory after start-up An allocation can fail, fragment the heap or take unpredictable time
Unbounded recursion and stack use The deepest stack must be provable before the device ships
Hidden control flow Exceptions and similar mechanisms make the paths through a function harder to analyse, which is why many embedded projects build without exceptions

Most of these were earlier lessons in this track; a guideline set turns their advice into rules a tool can check.

Checkpoint

Check: What safety guidelines remove

  1. Question 1 of 2 Which of these are undefined behaviour, the first concern of the guidelines?

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • A read one element past the end of an array
    • An int addition that overflows
    • Reading a local variable that was never given a value

    Undefined behaviour is what the standard sets no requirements for. Behaviour the implementation chooses is defined, but differently by each compiler or processor, so code that relies on one choice breaks on another.

  2. Question 2 of 2 Why do safety guidelines keep dynamic memory out of a program after start-up?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: An allocation can fail, fragment the heap or take unpredictable time

    An allocation that fails at the wrong moment, a heap that fragments over weeks, or an allocation whose time cannot be bounded are all hard to rule out by testing, so the rule removes them.

MISRA C++:2023 serves critical systems, the Core Guidelines everyday code

The two sets differ in who they are for and how a team gets them:

Aspect MISRA C++:2023 The C++ Core Guidelines
Published by MISRA, for C++17 The Standard C++ Foundation, for current C++
How to get it Bought from MISRA as a PDF or a printed copy Read online by anyone
Written for Critical systems, with a defined compliance process Everyday code
Checked by Analysers, with deviations recorded Compiler warnings, clang-tidy and code review

MISRA C++:2023 targets C++17 as ISO/IEC 14882:2017 defines it, and MISRA sells it as a PDF from its webstore and as a printed copy. MISRA had announced that its new edition would integrate the AUTOSAR C++ guidelines, and a corrected edition, technical corrigendum 1, changes wording without adding guidelines. Compliance is defined by a companion document, MISRA Compliance, which says how deviations are recorded and what a claim of compliance must state. Anyone can read the C++ Core Guidelines online; their licence allows copying, use and changes for personal or internal business use, and many of their rules have matching clang-tidy checks.

A project applies its guideline set the same way on every change: a plan, checks, and every finding either fixed or covered by a deviation record, all summed up for each release:

A guideline set leads to a project plan and checks; each finding is fixed or covered by a deviation record, then summarised.A guideline setMISRA C++:2023,plus the project's rulesThe project's plan:mandatory, required,advisory or disappliedChecks on every change:compiler warnings,analysers, code reviewA findingFix the codeRecord a deviation:why, the risk, theprecautions, whereA compliance summaryfor each releaseusuallythe rule cannotbe met here

How a project applies a guideline set, from the plan to the release

Text description of the diagram

The diagram is a flow from top to bottom.

  1. A guideline set: MISRA C++:2023, plus the project's own rules.
  2. The project's plan decides, for each guideline, whether it is mandatory, required, advisory or disapplied in this project.
  3. Checks run on every change: compiler warnings, analysers and the code review.
  4. Each check can produce a finding. Usually the code is fixed. When the rule cannot be met in that place, a deviation is recorded instead, drawn with a double border: why, the risk, the precautions and where it applies.
  5. Both paths end in a compliance summary for each release.

Checkpoint

Check: MISRA C++:2023 and the C++ Core Guidelines

  1. Question 1 of 2 Which edition of C++ does MISRA C++:2023 cover?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: C++17, as ISO/IEC 14882:2017 defines it

    MISRA C++:2023 targets C++17 as ISO/IEC 14882:2017 defines it, and MISRA sells it as a PDF and as a printed copy.

  2. Question 2 of 2 In a project that follows MISRA C++:2023, a rule cannot be met in one place. What does the project do?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It records a deviation that says why, the risk, the precautions and where it applies

    Each finding is either fixed or covered by a deviation record. MISRA Compliance, a companion document, says how deviations are recorded and what a claim of compliance must state.

Three checkers, three different slices of one function’s bugs

The draft averages a sensor’s last readings as a percentage of the maximum. All three checkers run on it:

draft.cpp C++ · draft.cpp
// A first draft: average the last readings of a sensor and scale the result to a percentage of the sensor's maximum.
#include <cstdint>
#include <print>

int* history = nullptr;  // a copy of the last readings, allocated on first use

std::uint8_t percent_of_max(const int* samples, int count, int max) {
    if (history == nullptr) history = new int[count];
    int sum = 0;
    for (int i = 0; i <= count; ++i) {  // one step too far
        history[i] = samples[i];
        sum += samples[i];
    }
    return sum * 100 / (count * max);
}

int main() {
    const int readings[4] = {2'000'000'000, 2'000'000'000, 1, 1};
    std::println("{}", percent_of_max(readings, 4, 4096));
}
Compiler warnings, clang-tidy and the sanitizers on one draft Python · checks_session.py
from session import run

# One draft function and three checkers, each with its full findings: the compiler's conversion warnings,
# clang-tidy (Homebrew's LLVM 23.1.2) with C++ Core Guidelines and bug-prone checks, and the sanitizers at run time.
run("""
clang++ -std=c++23 -Wall -Wextra -Wconversion -Wsign-conversion -fsyntax-only draft.cpp
clang-tidy --checks='-*,bugprone-*,cppcoreguidelines-*,misc-no-recursion,clang-analyzer-core.*' draft.cpp -- -std=c++23 -isysroot "$(xcrun --show-sdk-path)" 2>/dev/null | grep 'warning:'
clang++ -std=c++23 -g -fsanitize=address,undefined draft.cpp -o draft && ./draft 2>&1 | grep -E 'runtime error|SUMMARY'
""")

Output

$ clang++ -std=c++23 -Wall -Wextra -Wconversion -Wsign-conversion -fsyntax-only draft.cpp
draft.cpp:8:47: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
    8 |     if (history == nullptr) history = new int[count];
      |                                       ~~~     ^~~~~
draft.cpp:14:22: warning: implicit conversion loses integer precision: 'int' to 'std::uint8_t' (aka 'unsigned char') [-Wimplicit-int-conversion]
   14 |     return sum * 100 / (count * max);
      |     ~~~~~~ ~~~~~~~~~~^~~~~~~~~~~~~~~
2 warnings generated.
$ clang-tidy --checks='-*,bugprone-*,cppcoreguidelines-*,misc-no-recursion,clang-analyzer-core.*' draft.cpp -- -std=c++23 -isysroot "$(xcrun --show-sdk-path)" 2>/dev/null | grep 'warning:'
draft.cpp:5:6: warning: variable 'history' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]
draft.cpp:5:6: warning: variable 'history' provides global access to a non-const object; consider making the pointed-to data 'const' [cppcoreguidelines-avoid-non-const-global-variables]
draft.cpp:8:29: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' [cppcoreguidelines-owning-memory]
draft.cpp:11:9: warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]
draft.cpp:11:22: warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]
draft.cpp:12:16: warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]
draft.cpp:14:18: warning: 100 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers]
draft.cpp:17:5: warning: an exception may be thrown in function 'main' which should not throw exceptions [bugprone-exception-escape]
draft.cpp:18:11: warning: do not declare C-style arrays, use 'std::array' instead [cppcoreguidelines-avoid-c-arrays]
draft.cpp:19:39: warning: do not implicitly decay an array into a pointer; consider using gsl::array_view or an explicit cast instead [cppcoreguidelines-pro-bounds-array-to-pointer-decay]
draft.cpp:19:52: warning: 4096 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers]
$ clang++ -std=c++23 -g -fsanitize=address,undefined draft.cpp -o draft && ./draft 2>&1 | grep -E 'runtime error|SUMMARY'
draft.cpp:12:13: runtime error: signed integer overflow: 2000000000 + 2000000000 cannot be represented in type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior draft.cpp:12:13 
SUMMARY: AddressSanitizer: stack-buffer-overflow draft.cpp:11 in percent_of_max(int const*, int, int)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 checks_session.py

Step through the draft’s problems one at a time, each next to the checker that reported it:

Interactive · step by step Which checker found which problem of draft.cpp Step through the problems of the draft one at a time; each lands in the box of the checker that reported it, or in No checker.
Which checker found which problem of draft.cpp, step 9 of 9. Showing Compiler warnings (lines 8 and 14), clang-tidy (11 findings), The sanitizers (lines 11 and 12), No checker (3 problems).Compiler warningslines 8 and 14clang-tidy11 findingsThe sanitizerslines 11 and 12No checker3 problems

The drawing shows the whole scene; its 9 steps are listed below.

Every step as text
  1. Line 8, the size. new int[count] turns the signed count into the unsigned type of an allocation's size. The compiler's -Wsign-conversion reports it.
  2. Line 14, the result. sum * 100 / (count * max) is an int returned as a std::uint8_t, which loses precision; -Wimplicit-int-conversion reports it.
  3. The owning pointer. history = new int[count] gives a new allocation to a plain pointer that owns it. clang-tidy's check cppcoreguidelines-owning-memory reports it.
  4. Ten more. clang-tidy also reports the global, twice, the pointer arithmetic three times, two magic numbers, an exception that may escape main, the C array and the array that decays to a pointer: 11 findings in all.
  5. Line 12, at run time. Only a run shows that two readings of 2,000,000,000 overflow an int. UndefinedBehaviorSanitizer reported the signed overflow at line 12.
  6. Line 11, at run time. AddressSanitizer reported the read one past the end of samples at line 11, as a stack-buffer-overflow.
  7. Never run. The program never calls the function with count 0, so the division by zero ran nowhere, and neither the compiler nor clang-tidy proved the case.
  8. Two more. The memory that is never freed, and the history that keeps the size of the first call so that a later, longer call writes past it, were reported by no checker either.
  9. Slices. Each checker saw its own slice of the problems, and three were in no slice at all. A rule such as no dynamic memory after start-up removes a whole class of them instead.

The three checkers found different problems. The compiler’s conversion warnings flagged two implicit conversions, one into the size of the allocation and one that narrows the result to std::uint8_t. clang-tidy flagged the heap allocation owned by a plain pointer, the global, the pointer arithmetic and the C array. Only the sanitizers, while the program ran, found the signed overflow at line 12 and the read past the end at line 11. None of them reported the division by zero when count is 0, the memory that is never freed, or that history keeps the size of the first call, so a later, longer call writes past it. Guidelines exist because each tool sees only a slice: a rule such as “no dynamic memory after start-up” removes a whole class of bugs that no tool reliably finds.

Big Number Calculator Check 2,000,000,000 + 2,000,000,000 against the largest int, 2,147,483,647.

Key idea

Each checker sees only a slice of a function’s problems; a guideline that removes a whole class of constructs covers what none of them reaches.

Checkpoint

Check: What each checker can find

  1. Question 1 of 2 How many findings did clang-tidy report on draft.cpp in the recorded run?

    Type a number.

    A hint

    Count the lines that clang-tidy printed.

    Show the answer: Question 1 of 2

    Answer: 11 findings

    Eleven: the global twice, the owning pointer, three uses of pointer arithmetic, two magic numbers, an exception that may escape main, the C array and an array that decays to a pointer.

  2. Question 2 of 2 Why did none of the three checkers report the division by zero when count is 0?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The program never called the function with count 0, and neither static check proved the case

    Each tool sees only a slice: what the compiler can see in the types, what clang-tidy's checks look for, what a run happens to do. A rule that removes a class of bugs covers what no slice reaches.

Practise

Practice that adapts to you

Practise this lesson

10 questions about 9 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 10 Whether plain char is signed differs between compilers. To which concern of the list does relying on it belong?

    Choose one answer.

    Show the answer: Question 1 of 10

    Answer: Behaviour the implementation chooses

    Whether plain char is signed, how big long is and the order in which function arguments are evaluated are choices of the implementation.

  2. Question 2 of 10 Why do safety guidelines limit recursion?

    Choose one answer.

    Show the answer: Question 2 of 10

    Answer: The deepest stack must be provable before the device ships

    A device has a fixed stack. With unbounded recursion and stack use, no one can show before shipping that the stack is large enough for every input.

  3. Question 3 of 10 Many embedded projects build without exceptions. Which concern of the guidelines explains that choice?

    Choose one answer.

    Show the answer: Question 3 of 10

    Answer: Hidden control flow, which makes the paths through a function harder to analyse

    Exceptions and similar mechanisms make the paths through a function harder to analyse, which is why many embedded projects build without exceptions.

  4. Question 4 of 10 Why does this lesson describe the kinds of rules in MISRA C++:2023 instead of quoting them?

    Choose one answer.

    Show the answer: Question 4 of 10

    Answer: The document is sold by MISRA and its text is protected by copyright

    Anyone can buy and read the document, from MISRA's webstore as a PDF or printed on demand, but its wording is MISRA's. Describing what the guidelines aim at, in this lesson's own words, needs no copy of them.

  5. Question 5 of 10 What is a deviation in a MISRA-style project?

    Choose one answer.

    Show the answer: Question 5 of 10

    Answer: A documented decision to break one guideline in stated places, with the reason and the precautions

    Some guidelines cannot be met in some places, such as a cast that reaches a hardware register. A deviation record states which guideline, where, why, and what keeps the risk under control, so that a claim of compliance stays honest.

  6. Question 6 of 10 A team follows the C++ Core Guidelines and wants many of its rules checked on every change, without buying an analyser. What does the lesson point to?

    Choose one answer.

    Show the answer: Question 6 of 10

    Answer: clang-tidy, which has matching checks for many of the Core Guidelines' rules

    The C++ Core Guidelines are written for everyday code and checked by compiler warnings, clang-tidy and code review; MISRA C++:2023 is checked by analysers, with deviations recorded.

  7. Question 7 of 10 Which of the three checkers needs the program to run before it can report anything?

    Choose one answer.

    Show the answer: Question 7 of 10

    Answer: The sanitizers

    Compiler warnings and clang-tidy work from the source; the sanitizers report what a run does, which is why they found the overflow that only two readings of 2,000,000,000 cause.

  8. Question 8 of 10 In the recorded run, which checker reported the signed overflow in sum += samples[i]?

    Choose one answer.

    Show the answer: Question 8 of 10

    Answer: UndefinedBehaviorSanitizer, while the program ran

    Neither the compiler nor clang-tidy can see that two readings of 2,000,000,000 will be added. The sanitizer checked the addition when it happened and named line 12. Each checker sees a different slice of the problems.

  9. Question 9 of 10 Which problem of the draft did none of the three checkers report?

    Choose one answer.

    Show the answer: Question 9 of 10

    Answer: The division by zero when count is 0

    The test never called the function with count 0, so the sanitizers had nothing to observe, and neither static check proved the case. The read past the end was found by AddressSanitizer, the narrowing by the compiler and the allocation by clang-tidy.

  10. Question 10 of 10 The draft's memory is never freed, and no checker said so. What does that suggest about using guidelines as well as tools?

    Choose one answer.

    Show the answer: Question 10 of 10

    Answer: A rule such as no dynamic memory after start-up removes a whole class of bugs that no tool reliably finds

    Guidelines exist because each tool sees only a slice. Removing a whole class of constructs leaves nothing of that class for any tool to miss.

Rewriting the function step by step, with the checker output at each step Pro: opens with a Pro, Premium or Ultimate pass

The draft is rewritten here in three steps, one class of problem at a time, and the same three checkers run after each step. The output stays the same, 48 per cent, while the findings fall to zero.

In this part:

  • Step 1: removing the undefined behaviour
  • Step 2: making every conversion explicit and proved
  • Step 3: no heap, no globals, no pointer arithmetic
  • The findings at each step, measured
  • Recording a deviation, and auditing suppressions

1 diagram · 7 runnable examples (C++, Python, Shell) · about 7 minutes

Opening this part with your pass…

This part opens 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.

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).

Categories, analysers, C++26 checks, and a module to review Pro: opens with a Pro, Premium or Ultimate pass

A guideline set becomes a process once a project decides which rules may bend, knows what its tools can prove, and uses what the language now checks for it. This part covers all three, then hands you a module to review.

In this part:

  • Mandatory, required, advisory: deciding what may be broken
  • Decidable and undecidable rules, and what an analyser’s silence means
  • What C++26 adds: contracts and a hardened library, run
  • Your turn: review a module against the checklist
  • The model review, issue by issue

4 runnable examples (C++ (contract.cpp), C++ (hardened.cpp), C++ (valve.cpp), C++ (valve_test.cpp), Python) · about 9 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

  1. Fresher to mid · Warm-up

    What does a coding guideline set such as MISRA C++:2023 try to prevent, and why are tests alone not enough?

    It restricts C++ to a subset whose behaviour is defined, predictable and checkable: no undefined behaviour such as signed overflow or reading past an array, no reliance on choices the implementation makes, such as the signedness of plain char, no conversions that silently change a value, no dynamic memory after start-up, no unbounded recursion, and control flow simple enough to analyse. Tests alone are not enough because they show behaviour only for the inputs they try, and undefined behaviour can pass every test and still fail with another compiler, optimisation level or input. Guidelines remove whole classes of bugs instead of finding them one at a time, analysers check many of the rules on every change, and a written deviation covers each place where a rule cannot be met.

  2. 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?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. 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?

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    Does adopting C++26 contracts and a hardened standard library make a guideline set such as MISRA C++:2023 unnecessary?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Safety-critical C++ restricts the language to behaviour that is defined, predictable and checkable.
  • MISRA C++:2023 covers C++17 for critical systems; it is bought from MISRA, and its text is copyrighted.
  • Anyone can read the C++ Core Guidelines online, and many of their rules have clang-tidy checks.
  • Compiler warnings, analysers and sanitizers each find a different slice; guidelines remove whole classes of bugs.
  • Where a rule cannot be met, a deviation record states where, why and with which precautions.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.