MISRA C++ Expressions, Operators & Conversions
About 1992 wordsAbout 7 min
2026-03-25
Overview
Chapter 4 (Standard Conversions) and Chapter 5 (Expressions) of MISRA C++:2008 contain the largest cluster of rules. Implicit type conversions and complex expressions are among the most common sources of subtle bugs in C++ — especially on embedded targets where integer widths vary.
Essential Type Model
The essential type model (central to MISRA C++ and MISRA C) classifies every expression into one of these essential types:
| Essential Type | C++ Types Covered |
|---|---|
| Boolean | bool |
| Character | char, wchar_t |
| Signed integer | signed char, short, int, long, long long, int8_t, int16_t, int32_t, int64_t |
| Unsigned integer | unsigned char, unsigned short, unsigned int, unsigned long, uint8_t, uint16_t, uint32_t, uint64_t |
| Floating-point | float, double, long double |
| Enum | Any enum or enum class |
The rules restrict implicit conversions between essential types.
Implicit Conversion Rules
Rule 5-0-3 — No Implicit Conversion to Different Underlying Type (Required)
A cvalue expression shall not be implicitly converted to a different underlying type.
// NON-COMPLIANT: uint8_t + uint16_t — uint8_t promoted to int, then assigned to uint16_t
uint8_t a = 5U;
uint16_t b = 10U;
uint16_t c = a + b; // Non-compliant — a undergoes implicit int promotion
// COMPLIANT
uint16_t c = static_cast<uint16_t>(a) + b; // Compliant — explicit cast
// NON-COMPLIANT: narrowing
uint32_t big = 1000U;
uint8_t small = big; // Non-compliant — potential loss of data
// COMPLIANT
uint8_t small = static_cast<uint8_t>(big); // Compliant — explicit, documents intentRule 5-0-4 — No Implicit Signed/Unsigned Conversion (Required)
An implicit integral conversion shall not change the signedness of the underlying type.
// NON-COMPLIANT: int32_t assigned to uint32_t implicitly
int32_t signed_val = -1;
uint32_t unsigned_val = signed_val; // Non-compliant — sign change
// Result: unsigned_val == 0xFFFFFFFF (4294967295)
// COMPLIANT
uint32_t unsigned_val = static_cast<uint32_t>(signed_val); // Still wrong logically but compliant
// BETTER: redesign to avoid signed/unsigned mixingRule 5-0-5 — No Implicit Floating to Integer Conversion (Required)
There shall be no implicit floating-integral conversions.
// NON-COMPLIANT
float freq = 100.5F;
int32_t period = freq; // Non-compliant — truncates 100.5 to 100
// COMPLIANT
int32_t period = static_cast<int32_t>(freq); // Compliant — explicitRule 5-0-6 — No Implicit Conversion Decreasing Width (Required)
An implicit integral or floating-point conversion shall not reduce the size of the underlying type.
// NON-COMPLIANT: uint32_t to uint16_t (narrowing)
uint32_t val32 = 70000U;
uint16_t val16 = val32; // Non-compliant — truncates
// COMPLIANT
uint16_t val16 = static_cast<uint16_t>(val32); // Explicit — documents potential truncationRule 5-0-7 — No Non-Constant Expressions Composited with Chars (Required)
There shall be no explicit floating-integral conversions of a cvalue expression.
Rule 4-5-1 — bool Shall Not Be Used with Non-Boolean Operators (Required)
Expressions with type bool shall not be used as operands to built-in operators other than: =, &&, ||, !, ==, !=, unary &, and the conditional ?: operator.
// NON-COMPLIANT: using bool in arithmetic
bool flag = true;
int32_t count = flag + 1; // Non-compliant — +1 on bool
// NON-COMPLIANT
if (flag == 1) { ... } // Non-compliant — comparing bool to integer
// COMPLIANT
if (flag) { ... } // Compliant
if (!flag) { ... } // Compliant
bool result = flag && other; // CompliantRule 4-5-2 — enum Confined to Logical/Relational Operators (Required)
Expressions with type enum shall not be used as operands to built-in operators except [], =, ==, !=, unary &, <, <=, >, >=.
// NON-COMPLIANT: arithmetic on enum
enum class State { Idle = 0, Running = 1, Error = 2 };
State s = State::Idle;
int32_t n = s + 1; // Non-compliant — arithmetic on enum
// COMPLIANT: use underlying_type explicitly
int32_t n = static_cast<int32_t>(s) + 1; // Compliant — explicit conversionRule 4-10-1 — NULL Not Used as Integer (Required)
NULL shall not be used as an integer value.
// NON-COMPLIANT
int32_t x = NULL; // Non-compliant — NULL is an integer
// COMPLIANT
int32_t x = 0; // Compliant — use integer literal 0
int32_t* p = nullptr; // Compliant (C++11+) — use nullptr for pointersRule 4-10-2 — 0 Not Used as Null Pointer Constant (Required)
Literal zero shall not be used as the null-pointer-constant.
// NON-COMPLIANT (MISRA 2008, pre-C++11 mindset carried forward)
int32_t* p = 0; // Non-compliant — use nullptr
// NON-COMPLIANT
if (ptr == 0) { ... } // Non-compliant — compare with nullptr
// COMPLIANT
int32_t* p = nullptr; // Compliant
if (ptr == nullptr) { ... } // CompliantAssignment Operators
Rule 6-2-1 — No Assignment in Sub-Expressions (Required)
Assignment operators shall not be used in sub-expressions.
// NON-COMPLIANT: assignment inside condition
uint32_t val;
if ((val = read_sensor()) != 0U) { // Non-compliant
process(val);
}
// COMPLIANT
uint32_t val = read_sensor();
if (val != 0U) {
process(val);
}
// NON-COMPLIANT: chained assignment (may be acceptable in some contexts but Rule 6-2-1 restricts)
int32_t a, b, c;
a = b = c = 0; // Non-compliant — chained assignment is a sub-expressionRule 6-2-2 — Floating-Point Equality (Required)
Floating-point expressions shall not be directly or indirectly tested for equality or inequality.
// NON-COMPLIANT: exact floating-point comparison (unreliable due to rounding)
float measured = compute_value();
if (measured == 3.14159F) { ... } // Non-compliant
if (measured != 0.0F) { ... } // Non-compliant
// COMPLIANT: use a tolerance comparison
float epsilon = 1.0e-5F;
if (fabsf(measured - 3.14159F) < epsilon) { ... } // Compliant
if (fabsf(measured) > epsilon) { ... } // CompliantRule 6-2-3 — before++ / after++ (Advisory)
The ++ and -- operators should only appear as standalone statements, not in larger expressions.
// NON-COMPLIANT (advisory)
arr[i++] = val; // Non-compliant (advisory)
int32_t x = ++counter + 2; // Non-compliant (advisory)
// COMPLIANT
arr[i] = val;
i++; // Compliant — standalone statement
int32_t x = counter + 2;
++counter; // Compliant — separate statementUnary Operators
Rule 5-3-1 — Each Operand of ! Shall Have Boolean Type (Required)
// NON-COMPLIANT: ! applied to integer
uint32_t flags = get_flags();
if (!flags) { ... } // Non-compliant — !uint32_t reduces to bool via integral comparison
// COMPLIANT
if (flags == 0U) { ... } // Compliant — explicit comparisonRule 5-3-2 — Unary Minus Only on Signed Expressions (Required)
The unary minus operator shall not be applied to an expression whose underlying type is unsigned.
// NON-COMPLIANT
uint32_t u = 5U;
int32_t n = -u; // Non-compliant — unary minus on unsigned is implementation-defined
// COMPLIANT
int32_t n = -static_cast<int32_t>(u); // Compliant — convert firstShift Operators
Rule 5-8-1 — Shift Count Within Type Width (Required)
The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the underlying type of the left hand operand.
// NON-COMPLIANT: shifting by too many bits is undefined behaviour
uint8_t byte_val = 0x01U;
uint32_t result = byte_val << 8U; // Non-compliant — shifts past width of uint8_t
// COMPLIANT: widen before shifting
uint32_t result = static_cast<uint32_t>(byte_val) << 8U; // CompliantBitwise Operators
Rule 5-0-21 — Bitwise Operations on Signed Types (Required)
Bitwise operations shall not be performed on signed integer types.
// NON-COMPLIANT: bitwise AND on signed int
int32_t val = 0x12345678;
int32_t masked = val & 0xFF; // Non-compliant — bitwise op on signed type
// COMPLIANT
uint32_t uval = static_cast<uint32_t>(val);
uint32_t masked = uval & 0xFFU; // Compliant — bitwise on unsignedLogical Operators
Rule 5-2-1 — Each Operand of && and || Shall Be Boolean (Required)
Each operand of the && or || operators shall be a postfix-expression.
// NON-COMPLIANT: non-boolean operands
uint32_t a = 5U, b = 3U;
if (a && b) { ... } // Non-compliant — a and b are integers
// COMPLIANT: convert to boolean explicitly
if ((a != 0U) && (b != 0U)) { ... } // CompliantRule 5-14-1 — No Side Effects in Right Operand of Logical && or || (Required)
The right operand of && or || shall not contain side effects.
// NON-COMPLIANT: side effect in right operand — right side may not be evaluated (short-circuit)
bool result = is_valid() && (++counter > 0); // Non-compliant — ++counter may not execute
// COMPLIANT: separate the side effect
++counter;
bool result = is_valid() && (counter > 0); // CompliantConditional Operator
Rule 5-0-13 — Condition in Conditional Operators (Required)
The condition of a ternary ? : shall be a Boolean expression.
// NON-COMPLIANT: condition is integer
uint32_t len = 10U;
uint32_t result = len ? process() : 0U; // Non-compliant
// COMPLIANT
uint32_t result = (len != 0U) ? process() : 0U; // Compliantsizeof Operator
Rule 5-3-4 — sizeof Applied to Expressions Then → sizeof(Type) (Required)
The sizeof operator shall not be applied to an expression that contains a side effect.
// NON-COMPLIANT: side effect inside sizeof (side effect is never executed)
sizeof(arr[i++]); // Non-compliant — i++ inside sizeof is never evaluated
// COMPLIANT
sizeof(arr[0]); // Compliant — no side effect
sizeof(int32_t); // Compliant — type, not expressionComma Operator
Rule 5-18-1 — No Comma Operator in Expressions (Required)
The comma operator shall not be used.
// NON-COMPLIANT
for (int32_t i = 0, j = 10; i < j; ++i, --j) { ... } // Non-compliant — comma in for
// Exception: commas in for-loop initialisation and increment are acceptable
// Rule 5-18-1 strictly means comma operator in expressions like: (a++, b++)
int32_t val = (do_a(), do_b()); // Non-compliant — comma operator
// COMPLIANT
do_a();
int32_t val = do_b(); // CompliantCast Operators
Rule 5-2-4 — Only C++ Style Casts (Required)
C-style casts and functional notation casts shall not be used.
// NON-COMPLIANT: C-style cast
uint32_t u = (uint32_t)some_int; // Non-compliant
// NON-COMPLIANT: functional notation cast
uint32_t u = uint32_t(some_int); // Non-compliant
// COMPLIANT: C++ named casts
uint32_t u = static_cast<uint32_t>(some_int); // CompliantRule 5-2-5 — No Cast to Remove const or volatile (Required)
A cast shall not remove any const or volatile qualification from the type of a pointer or reference.
// NON-COMPLIANT: const_cast to remove const
const uint8_t* const_ptr = get_data();
uint8_t* mutable_ptr = const_cast<uint8_t*>(const_ptr); // Non-compliant
*mutable_ptr = 0; // Writes to data that should be read-only — UB possible
// COMPLIANT: redesign to avoid needing const_cast
// If you need to modify data, it should not be constRule 5-2-6 — No reinterpret_cast (Required)
A cast shall not convert a pointer to a function to any other pointer type, including a pointer to function type.
Note: reinterpret_cast is highly restricted in MISRA C++. The only accepted use is for converting between pointer types when interacting with hardware registers — and this must be deviations-documented.
// NON-COMPLIANT: reinterpret_cast between unrelated types
uint32_t raw = reinterpret_cast<uint32_t>(ptr); // Non-compliant
// NON-COMPLIANT: accessing hardware via reinterpret_cast without deviation
volatile uint32_t* reg = reinterpret_cast<volatile uint32_t*>(0x40021000);
// COMPLIANT (with deviation): document hardware register access
// Deviation reference: DEV-2024-007
// Justification: Required to access memory-mapped hardware register at 0x40021000
// Risk: implementation-defined alignment conversion
// Mitigation: address is always aligned to 4-byte boundary per hardware spec
volatile uint32_t* reg = reinterpret_cast<volatile uint32_t*>(0x40021000U);Summary: Key Expression Rules
| Rule | Level | Summary |
|---|---|---|
| 4-5-1 | Required | No non-boolean operators on bool |
| 4-5-2 | Required | No arithmetic on enum |
| 4-10-1 | Required | NULL not used as integer |
| 4-10-2 | Required | 0 not used as null pointer |
| 5-0-3 | Required | No implicit type change between essential types |
| 5-0-4 | Required | No implicit signed/unsigned conversion |
| 5-0-5 | Required | No implicit float→int conversion |
| 5-0-6 | Required | No implicit narrowing conversion |
| 5-2-4 | Required | Only C++ style casts (no C-style) |
| 5-2-5 | Required | No cast that removes const/volatile |
| 5-3-1 | Required | ! only on boolean types |
| 5-3-2 | Required | Unary minus only on signed operands |
| 5-8-1 | Required | Shift count within bitwidth of type |
| 5-0-21 | Required | No bitwise ops on signed types |
| 5-14-1 | Required | No side effects in &&/` |
| 5-18-1 | Required | No comma operator |
| 6-2-1 | Required | No assignment in sub-expressions |
| 6-2-2 | Required | No floating-point equality test |
| 6-2-3 | Advisory | ++/-- as standalone statements only |