Visual walkthrough — MISRA C — rules for safety-critical C code
The parent note said: "Only use bitwise ops on unsigned types where the bit pattern is standardized." That is the central result of MISRA C's type-safety rules. But why is the bit pattern of a signed number dangerous? This page rebuilds the whole argument from the ground up — from "what is a bit" all the way to "why (-1) >> 1 is a landmine" — one picture at a time.
We will earn every symbol before we use it. If you have never seen a bit, a shift, or two's complement, start at line one and you will be fine.
Prerequisites we will link but not assume: Embedded C Best Practices, Memory Safety, Static Analysis Tools. Parent: MISRA C parent (Hinglish).
Step 1 — What is a bit, and what is a "bit pattern"?
WHAT. A computer stores every number as a row of tiny switches. Each switch is either off (we write 0) or on (we write 1). One switch = one bit. A fixed-width row of these switches — say 8 of them — is a bit pattern.
WHY. Before we can talk about "shifting bits" or "the sign bit", we must see the row of switches literally. Every later step is just moving these switches around.
PICTURE. Below, an 8-bit row. Each cell is one bit. The number 13 lives here as the pattern 00001101.

Reading the row like ordinary base-10, but in base-2:
- Each label under a bit is its place value (a power of two).
- Add up the place values wherever a
1sits: . That is the number.
Step 2 — What does the shift operator >> actually do?
WHAT. The operator >> (say "right shift") takes the whole row of bits and slides every bit one position to the right. Bits that fall off the right edge vanish. A new bit must appear on the left to fill the gap.
WHY. MISRA Rule 10.1 is about << and >> (and &, |, ^). To judge whether the rule is worth having, we must watch exactly what a shift does to the row — especially what fills the empty left slot, because that is where the danger hides.
PICTURE. Watch 00001101 (=13) become 00000110 (=6) under one right shift. The rightmost 1 falls off; a 0 slides in on the left.

The empty-left-slot question — what comes in on the left? — was easy here: a 0. Hold that thought. For unsigned numbers the answer is always 0, and that predictability is the whole point of the rule.
Step 3 — Where do negative numbers live? (Two's complement, the sign bit)
WHAT. A row of bits has no minus sign to spare. So to store negatives, hardware uses a convention called two's complement: the leftmost bit is reinterpreted as a negative place value. In 8 bits, the leftmost bit is worth instead of .
WHY. Rule 10.1 forbids shifts on signed operands specifically. We cannot see the danger until we see how "signed" changes the meaning of the very same row of switches. Same switches, new rulebook.
PICTURE. The identical row, now with the leftmost place value flipped to . The number is the pattern where every bit is 1.

Check that all-ones really is :
- The leftmost
1contributes (the sign bit "turned on"). - The other seven
1s contribute . - Total: . ✔
So the pattern 11111111 is the number . Remember that; it is our trap in Step 5.
Step 4 — The fork in the road: two ways to fill the left slot
WHAT. When you right-shift a signed negative number, hardware designers face a choice for the incoming left bit:
- Logical shift: always slide in a
0. - Arithmetic shift: slide in a copy of the sign bit (a
1if the number was negative), so the number stays negative and behaves like true division.
WHY. This is the exact hinge of the whole rule. The C standard does not decide which one happens for signed types — it is left implementation-defined, meaning the compiler vendor picks. Two correct compilers can disagree. Same source code, different result.
PICTURE. Both forks drawn side by side for -1 >> 1. Left fork (logical) fills 0 and gives a huge positive number; right fork (arithmetic) fills 1 and gives back.

The two forks side by side:
- Left branch: a
0came in on top, killing the sign bit → result reads as (on 8-bit; on 32-bit it becomes the giant ). - Right branch: a copy of the sign bit
1came in → result stays .
Both are legal C. That ambiguity is not acceptable in a braking system.
Step 5 — The landmine in code: (-1) >> 1
WHAT. We combine everything. A programmer writes int result = (-1) >> 1; expecting "half of , rounded toward zero" — i.e. , or on arithmetic hardware . But on a compiler that does logical shifts of signed types, they get (the largest 32-bit signed value).
WHY. This is the concrete disaster Rule 10.1 prevents. A value expected to be near zero suddenly becomes over two billion. If that value feeds a loop bound, a memory index, or a timer, you have a hang, an out-of-bounds write (a Memory Safety breach), or a missed real-time deadline in an Real-Time Operating Systems (RTOS).
PICTURE. A number line showing the expected result ( or , near the origin) versus the possible actual result (, off at the far right), with the gap labelled "silent catastrophe".

int32_t x = -1;
int32_t r = x >> 1; /* Rule 10.1 VIOLATION: signed operand of >> */
/* r is -1 on one compiler, 2147483647 on another. Both are legal C. */Step 6 — The MISRA fix: shift only unsigned operands
WHAT. MISRA C:2012 Rule 10.1 forbids bitwise and shift operators on operands of essentially signed type. The fix: make the operand unsigned. For unsigned types the C standard guarantees the left slot fills with 0 — the logical shift — on every compiler.
WHY. Removing the ambiguity restores predictability (a core MISRA principle): the same code gives the same bit pattern everywhere. Now the value is verifiable, testable, and safe to reason about with Formal Verification.
PICTURE. The unsigned world: only one fork exists. 11111111u (= 255) >> 1 always → 01111111 (= 127). No choice, no vendor gamble.

uint32_t x = 0xFFFFFFFFU; /* the unsigned value 4294967295 */
uint32_t r = x >> 1; /* Rule 10.1 COMPLIANT */
/* r is 0x7FFFFFFF = 2147483647 on EVERY compiler. Deterministic. */Note that 0xFFFFFFFFU as unsigned means , and >> 1 gives — a defined answer, unlike the signed trap in Step 5 even though the bit pattern looks similar.
Step 7 — Edge & degenerate cases (so nothing surprises you)
WHAT. Let us sweep the corners the rule also protects.
WHY. A rule you only understand in the "typical" case will bite you at the boundaries. MISRA's value is that it covers all of them uniformly by banning signed operands outright.
PICTURE. A small table of corner cases, each with its "signed = ambiguous / unsigned = defined" verdict.

| Expression | Signed operand (banned) | Unsigned operand (compliant) |
|---|---|---|
0 >> 1 |
Defined () but still banned for uniformity | , defined |
x >> 0 (zero shift) |
No-op, but signedness still checked | , defined |
x >> 32 (width shift) |
Undefined behaviour — shift width | Still undefined — Rule 12.2 bans it separately |
-1 >> 1 (negative) |
The trap: or | n/a (make it unsigned first) |
x << 1 on large x |
Can overflow into/through sign bit → UB | Wraps modulo , defined |
The one-picture summary
Everything above collapses into one diagram: the same 8-bit row, right-shifted once, forking on signedness — unsigned always safe (one deterministic path), signed branching into a vendor gamble.

Recall Feynman retelling — say it back in plain words
A number in a computer is just a row of on/off switches. When you "shift right", you slide every switch one seat to the right; someone falls off the end, and a new switch appears on the left. The only question that matters is: what is that new left-hand switch? For a normal, never-negative number the answer is always "off" — a 0 — and everyone agrees, so shifting just halves the number, everywhere, always.
But negative numbers are stored with a trick: the leftmost switch secretly means "minus a big amount". Now when you shift right, the machine has a choice for the new left switch — slide in a plain 0, or copy the old sign switch to keep the number negative. The C language refuses to pick for signed numbers, so it lets each compiler decide. That is why (-1) >> 1 can be on one machine and over two billion on another — with no warning at all.
MISRA Rule 10.1 removes the choice by removing the temptation: only shift unsigned numbers, where the language promises the new switch is 0 on every compiler. One rule, and the ambiguity is gone — your code means the same thing everywhere, which is exactly what a brake controller needs.
Recall Quick self-test
What comes into the empty left slot when you right-shift an unsigned value? ::: Always a 0 (logical shift), guaranteed by the C standard on every compiler.
Why is (-1) >> 1 dangerous? ::: The fill bit for a signed shift is implementation-defined, so the result is either (arithmetic) or a huge positive number (logical) depending on the compiler.
Which MISRA C:2012 rule bans bitwise/shift ops on essentially signed operands? ::: Rule 10.1 (Required).
Is 0 >> 1 on a signed zero allowed? ::: No — the rule judges the operand's type, not its value, so signed zero is still a violation (this keeps the rule automatically decidable).
What separate rule handles shifting by more than the type's width? ::: Rule 12.2 (a shift count must be less than the operand's width; otherwise it is undefined behaviour).