Worked examples — MISRA C — rules for safety-critical C code
5.5.19 · D3· Coding › Embedded Systems & Real-Time Software › MISRA C — rules for safety-critical C code
Yeh page parent note ke rules ko har tarah ki situation ke through drill karti hai jo ek real code review mein aa sakti hai. Shuru karne se pehle, ek vaada: yahan kuch bhi assume nahi kiya ki aap C ke traps pehle se "samajhte" hain. Har example trap ko scratch se rebuild karta hai, landmine dikhata hai, phir use MISRA ke tarike se defuse karta hai.
The scenario matrix
Is topic ka har trap ek case class mein aata hai. Neeche ke examples un cells ke saath labelled hain jo woh cover karte hain, toh saath milaakar poora grid fill ho jaata hai.
| # | Case class | Mushkil kyun hai | Covered by |
|---|---|---|---|
| A | Signed → unsigned conversion | negative value silently wrap hokar ek bada positive ban jaata hai | Ex 1 |
| B | Narrowing / truncation | ek wide value narrow type mein squeeze hoti hai aur top bits kho deti hai | Ex 2 |
| C | Signed shift (sign-bit case) | negative number ko right-shift karna implementation-defined hai | Ex 3 |
| D | Pointer arithmetic across arrays | pointer apni array se bahar walk kar jaata hai | Ex 4 |
| E | Dangling pointer (degenerate: object already dead) | ek local ka address wapas return hota hai uske khatam hone ke baad | Ex 5 |
| F | Dynamic memory (banned resource) | malloc fail / fragment / non-deterministic ho sakta hai |
Ex 6 |
| G | Control-flow ambiguity (dangling else / missing braces) | code dikhta hai nested lekin hai nahi | Ex 7 |
| H | Real-world word problem (sensor scaling with overflow) | intermediate arithmetic overflow ho jaati hai notice karne se pehle | Ex 8 |
| I | Exam-style twist (spot-the-category) | rule classify karein + Mandatory / Required / Advisory choose karein | Ex 9 |
| Z | Zero / limiting / boundary inputs | 0 par value, type max par, "zero se neeche", "one past the end" |
Ex 1 (Step 5), Ex 4, Ex 8 |
Vault se prerequisite ideas jinpar hum rely karte hain: Memory Safety, Embedded C Best Practices, Static Analysis Tools.
Example 1 — Cell A & Z: signed → unsigned (zero/limit boundary ke saath)
Step 1 — Draw karein ki "unsigned" ka matlab kya hai. Figure s01 dekho neeche — "Unsigned int is a RING": yeh se tak ke values ka ek circle draw karta hai bina negative side ke, ek amber arrow ke saath jo dikhata hai kahan land karta hai. Ek unsigned int ek clock ki tarah wrap around karta hai: zero se neeche koi jagah nahi hai.

unsigned int values ; top-left par amber dot woh jagah hai jahan wrap hota hai, yaani . Deliberately koi bottom half nahi hai: negatives ke paas jaane ki jagah nahi.
Yeh step kyun? Conversion ke baare mein reason tab tak nahi kar sakte jab tak aap dekh nahi lete ki unsigned ring par ke liye koi jagah nahi hai — toh C use "zero se ek step neeche" bhejta hai, jo top par wrap ho jaata hai.
Step 2 — define karein, phir C ka conversion rule apply karein. ko target type ki bits mein width maano — 32-bit unsigned int ke liye, , toh ring mein slots hain. C ek signed value ko unsigned type mein convert karta hai lekar ("ring par wrap" operation). Yahan:
Yeh step kyun? Hum explicitly name karte hain taaki formula kisi bhi unsigned width par generalize ho sake (8-bit type use karega, unsigned long shayad ). Yahi exact rule hai jo compiler silently follow karta hai — koi warning nahi, koi error nahi — aur engineer ne jo "error value" socha tha woh actually sabse bada possible number hai.
Step 3 — MISRA rule ka naam lo. Ek essentially signed constant (-1) ko ek essentially unsigned target ke saath mix karna Rule 10.3 (Required) ka violation hai essential type model ka.
Yeh step kyun? Rule ka naam lena "yeh galat lagta hai" ko ek citable review finding mein badal deta hai jo ek static analyser automatically flag karega.
Step 4 — Compliant fix. Value ko visible aur intentional banao:
unsigned int u = 0xFFFFFFFFU; /* value explicitly stated, reviewer sees intent */Yeh step kyun? Intended bit pattern directly likhna hidden conversion ko remove karta hai — reviewer ab exactly woh number dekhta hai jo store hota hai, toh kuch bhi inference par nahi chhuta.
Step 5 — Zero/limit boundary (Cell Z, matrix mein vaada kiya tha). Dangerous edge wahan hai jahan ring zero se neeche cross karti hai. Kyunki koi negative side nahi hai, 0U - 1U nahi hai -1 — yeh hai 0xFFFFFFFF (ring ke top par wapas wrap). Classic consequence: loop for (unsigned i = n; i >= 0; i--) kabhi terminate nahi hoti, kyunki i == 0U ke baad decrement par wrap ho jaata hai aur condition i >= 0 unsigned value ke liye hamesha true hoti hai.
Yeh step kyun? Scenario matrix ne Cell Z ke liye yahan ek boundary case ka vaada kiya tha; yeh step wahi case hai, seedha usi ring picture se tied — infinite loop sirf ring hai jo zero se past galat direction mein traverse ho rahi hai.
Recall Reveal — Example 1 answer
u hold karta hai ::: 0xFFFFFFFF = 4294967295 (na -1, na error); rule violated hai 10.3 (Required)
0U - 1U equals ::: 0xFFFFFFFF, isliye unsigned down-counting loops kabhi nahi rukti
Verify: , aur . ✓ (VERIFY mein check kiya).
Example 2 — Cell B: narrowing / truncation
Step 1 — Bits count karein. uint8_t mein 8 bits hain, toh yeh distinct values hold karta hai: se tak. 300 ko 9 bits chahiye.
Yeh step kyun? Hume destination ki capacity pata honi chahiye pehle overflow dekhne se; truncation sirf low 8 bits rakhta hai aur bit 7 se upar sab kuch silently throw away karta hai.
Step 2 — Modulo karo. Low 8 bits rakhna exactly hai:
Toh small == 44, beginner jo "clamped" 255 expect karta hai woh nahi.
Yeh step kyun? High bits discard karna mathematically ke identical hai; ise compute karna surprising real value dikhata hai aur intuitive "yeh bas 255 par cap ho jaata hai" guess ko disprove karta hai.
Step 3 — Rule. Ek wider value ko narrower essential type mein narrow karna Rule 10.3 (Required) violation hai (ek assignment jo essential type category aur possibly value dono change karta hai).
Yeh step kyun? Example 1 jaisa hi rule family, lekin uska narrowing face — dono faces ko recognise karna matlab aap review time par koi bhi catch kar sakte hain.
Step 4 — Compliant fix: explicitly clamp karein taaki intent code mein ho, kismet mein nahi:
uint16_t v = 300U;
uint8_t small = (v > 255U) ? 255U : (uint8_t)v; /* explicit clamp */Yeh step kyun? Ek explicit clamp out-of-range decision ko visible aur deliberate banata hai (yahan yeh sach mein 255 par cap karta hai) silent truncation 44 par rely karne ki jagah.
Recall Reveal — Example 2 answer
(uint8_t)300 deta hai ::: 44 (low 8 bits: ), 255 NAHI aur error bhi NAHI
Rule violated ::: 10.3 (Required), essential types ke across narrowing
Verify: . ✓
Example 3 — Cell C: signed right-shift (sign-bit case)
Step 1 — Kyun bitwise ops signedness ki parwah karte hain. Shift number ko ek bit pattern ki tarah treat karta hai. Negative number ke liye top bit (sign bit) 1 hai, aur standard yeh fix nahi karta ki right shift par khali hue top bits mein kya fill hoga.
Yeh step kyun? Poora trap us ek unfixed detail mein rehta hai — jab tak aap nahi dekhte ki "top mein kya fill hota hai" signed values ke liye standardised nahi hai, danger invisible rehta hai.
Step 2 — Dono legal behaviours.
- Arithmetic shift (sign bit copy karta hai): result negative rehta hai, .
- Logical shift (0 se fill karta hai): result ek bada positive number ban jaata hai.
Yeh step kyun? Dono permitted outcomes list karna yahi point hai: kyunki C standard dono allow karta hai, (-8) >> 1 ki value implementation-defined hai — compiler X par aapka test pass ho sakta hai jabki wahi code compiler Y par silently change ho jaata hai. Wahi non-portability exactly woh hai jo MISRA ban karta hai.
Step 3 — Rule. Rule 10.1 (Required): shift/bitwise operators essentially signed operands par apply nahi kiye jaane chahiye. (MISRA C:2004 mein yeh Rule 12.7 tha.)
Yeh step kyun? Rule cite karna portability worry ko ek concrete, tool-checkable finding mein convert karta hai.
Step 4 — Compliant fix, aur kyun yeh compliant hai. Sirf unsigned bit patterns shift karein:
uint32_t v = 0xFFFFFFF8U; /* -8 ke bits, ab ek unsigned value */
uint32_t r = v >> 1; /* well-defined: 0x7FFFFFFC */Yeh step kyun? Yeh fix MISRA compliant hai exactly isliye kyunki dono operands ab essentially unsigned hain, toh Rule 10.1 ka prohibition ab apply nahi hota — aur unsigned right-shift ke liye C standard guarantee karta hai zero-fill (logical shift), toh khali hua top bit har compiler par 0 se fill hoga. Koi sign bit copy karne ke liye nahi, toh Step 2 ka implementation-defined choice disappear ho jaata hai aur result 0x7FFFFFFC portable hai.
Recall Reveal — Example 3 answer
(-8) >> 1 result ::: implementation-defined (arithmetic deta hai, logical ek bada positive deta hai) — isliye banned hai
Unsigned 0xFFFFFFF8U >> 1 ::: 0x7FFFFFFC = 2147483644, guaranteed zero-fill, MISRA compliant
Verify: arithmetic-shift result hai; unsigned `0xFFFFFFF8U >> 1 = 0x7FFFFFFC = 2147483644$. Dono checked. ✓
Example 4 — Cell D & Z: pointer arithmetic aur "one past the end"
Step 1 — Array ko fenced land ki tarah picture karein. Figure s02 dekho — "legal pointer targets for int a[4]": real elements ke liye chaar cyan boxes plus ek dashed amber "gate" box last wale ke thoda past, illegal targets par red ✗ marks ke saath.

int a[4] chaar solid cyan cells a[0]…a[3] ki tarah draw kiya, har ek apne offset a+0…a+3 ke saath labelled. Right par dashed amber cell a+4 hai, "one-past-the-end" gate (form aur compare kiya ja sakta hai, kabhi dereference nahi). Red ✗ a-1 aur a+5 par woh offsets mark karte hain jo Rule 18.1 forbid karta hai.
Yeh step kyun? Bounds abstract rehte hain jab tak aap fence nahi dekhte; picture special "one gate cell past the end" exception ko concrete banati hai memorise karne ki chez ki jagah.
Step 2 — Har pointer classify karein.
| Expression | Kahan points karta hai | Legal to form? |
|---|---|---|
a + 0 |
element 0 | ✓ |
a + 3 |
element 3 (last real) | ✓ |
a + 4 |
one-past-the-end (the gate) | ✓ (form/compare only) |
a + 5 |
gate se aage | ✗ Rule 18.1 violation |
a - 1 |
array se pehle | ✗ Rule 18.1 violation |
Yeh step kyun? Har offset enumerate karna — including dono boundary offsets a+4 aur a-1 (Cell Z) — yeh guarantee karta hai ki koi reader kisi unshown case se na takraye.
Step 3 — Rule. Rule 18.1 (Required): arithmetic se produce kiya gaya pointer usi array ke kisi element ko address karta rehna chahiye (ya uske ek past end).
Yeh step kyun? Rule us invariant ko name karta hai jo table test kar rahi hai, toh aap ise kisi bhi size ke arrays par apply kar sakte hain, sirf is ek par nahi.
Step 4 — Kyun compiler ise check nahi kar sakta. Pointer function parameter ki tarah aa sakta hai bina kisi size info ke attached; bounds compile time par unknown hote hain. MISRA isliye construction restrict karta hai, koi baad ka runtime check nahi. Dekho Memory Safety.
Yeh step kyun? Yeh samajhna ki restriction pointer form karne par kyun hai (kisi baad ke runtime check par nahi) explain karta hai kyun MISRA rules conservative dikhte hain — tool literally bound jaanta hi nahi.
Recall Reveal — Example 4 answer
Legal to form ::: a+0, a+3, a+4 (one-past-end, form/compare only)
Illegal (Rule 18.1) ::: a+5 aur a-1
Verify: int a[4] ke saath, legal offsets hain (count ); illegal chote examples hain aur . ✓
Example 5 — Cell E: dangling pointer (degenerate — object already dead)
Step 1 — Lifetime trace karein. x ka automatic storage hai — yeh f ke stack frame par rehta hai aur f return hote hi destroy ho jaata hai. Address &x ab freed stack space point kar raha hai.
Yeh step kyun? Pointer f ke andar galat nahi hai; yeh exactly return par dangling pointer banta hai. Lifetime trace karna us moment ko pinpoint karta hai jab object mar jaata hai — yeh woh degenerate case hai jahan object use hone se pehle dead hai.
Step 2 — Caller kya padhta hai. Undefined behaviour: shayad 10 padhe, garbage padhe, ya crash ho — often debug builds mein "kaam karta hai" aur optimisation ke baad fail hota hai. Kabhi rely mat karo.
Yeh step kyun? Teeno possible outcomes dikhana reader ko yeh conclude karne se rokta hai ki "mere machine par 10 print hua, toh theek hai".
Step 3 — Rule. Rule 18.7 (Required) — ek automatic object ka address object se zyada nahi jeena chahiye. (MISRA C:2004 mein analogous restriction Rule 17.6 tha.)
Yeh step kyun? Rule (aur uska 2004 predecessor) name karna aapko ise standard ke either version mein dhundhne deta hai.
Step 4 — Compliant fix: caller ko storage own karne do:
void f(int *out) { *out = 10; } /* caller live memory supply karta hai */Yeh step kyun? Ownership caller par shift karna guarantee karta hai ki storage write se zyada jeega, jo is version ko compliant banata hai.
Recall Reveal — Example 5 answer
Caller jo value padhta hai ::: undefined behaviour (shayad 10, shayad garbage, shayad crash) — kabhi rely mat karo Rule violated ::: 18.7 (Required); fix = caller storage pass karta hai
Verify: conceptual (koi numeric answer nahi) — checkable fact object count/lifetime logic hai, structurally VERIFY mein True verify kiya.
Example 6 — Cell F: dynamic memory (banned resource)
Step 1 — Heap kyun banned hai. malloc (1) runtime par fail kar sakta hai, (2) memory fragment kar sakta hai toh ek baad ki allocation fail ho jaati hai chahe free space ho, aur (3) non-deterministic time leta hai — ek hard RTOS deadline mein fatal.
Yeh step kyun? Aap ek alag design justify tab tak nahi kar sakte jab tak woh teeno failure modes clearly naa dekh lo jo heap introduce karta hai.
Step 2 — Rule. Rule 21.3 (Required): <stdlib.h> ke allocation/deallocation functions use nahi kiye jaane chahiye.
Yeh step kyun? Rule woh citation hai jo aapki safety case ko chahiye; yeh exactly woh functions name karta hai (malloc, calloc, realloc, free) jo off-limits hain.
Step 3 — Fixed-bound static allocation. Kyunki n ≤ 256 jaana hua hai, worst case pehle se reserve karein:
#define MAX_N 256U
static int buf[MAX_N]; /* worst case ke liye sized, ek baar allocate */
/* buf[0 .. n-1] use karein, n <= MAX_N ek check se enforce kiya */Yeh step kyun? Compile time par worst case reserve karna heap ke teeno failure modes remove karta hai: memory poore program lifetime tak exist karti hai, fragment nahi ho sakti, aur uska size statically provable hai.
Step 4 — Boundary check (Cell Z phir se): indexing se pehle n > MAX_N reject karein.
Yeh step kyun? Static storage sirf tab protect karta hai jab n reserved bound se zyada na ho; guard hi woh cheez hai jo guarantee runtime par true rakhta hai.
Recall Reveal — Example 6 answer
malloc ki jagah ::: ek fixed-size static array worst case ke liye sized (static int buf[256]), plus ek runtime check n <= MAX_N
Rule ::: 21.3 (Required)
Worst-case size ::: sizeof(int) par depend karta hai; 4-byte-int platform par, bytes
Verify: worst-case footprint ; 4-byte-int platform par woh bytes hai; 2-byte-int platform par bytes hai. Exactly ek baar allocate. ✓
Example 7 — Cell G: control-flow ambiguity (missing braces)
Step 1 — Ise waise padhein jaise compiler padhta hai. Indentation compiler ke liye ek jhooth hai. if ke baad sirf pehla statement conditional hai:
if (x > 0) { y = 1; }
z = 2; /* HAMESHA run karta hai */Toh x = 0 ke liye: y untouched hai, aur z = 2 regardless run karta hai.
Yeh step kyun? Bug poori tarah ek mismatch hai ki human indentation kaise padhta hai aur compiler statements kaise padhta hai; ise compiler ke tarike se rewrite karna mismatch visible karta hai.
Step 2 — Rule. Rule 15.6 (Required): ek if/loop ka body ek compound statement (braced) hona chahiye. Braces "if ke andar kya hai" ko unambiguous banate hain aur dangling-else class ke bugs khatam karte hain.
Yeh step kyun? Rule name karna dikhata hai ki yeh ek style preference nahi balki ek required, tool-enforced guard hai exactly is ambiguity ke against.
Step 3 — Compliant version:
if (x > 0) {
y = 1;
}
z = 2; /* clearly unconditional */Yeh step kyun? Braces ke saath, human jo code dekhta hai aur compiler jo code run karta hai identical hain — jo Rule 15.6 ka poora point hai.
Recall Reveal — Example 7 answer
x = 0 ke liye ::: y unchanged hai (uski prior value), z == 2 regardless run karta hai — z = 2 kabhi if ke andar tha hi nahi
Rule ::: 15.6 (Required), mandatory braces
Verify: x = 0 ke liye, z == 2 aur y unchanged (uski prior value). VERIFY mein numerically checked. ✓
Example 8 — Cell H (word problem) & Z: sensor scaling jo overflow hoti hai
Step 1 — Intermediate ko type limit se compare karein. uint16_t max par hai. Lekin
jo bahut zyada bada hai — multiply divide se pehle overflow (wraps mod ) ho jaata hai. Galat answer, silently.
Yeh step kyun? Bug intermediate value mein chhupa hai, final mein nahi; us intermediate ko type ke max se compare karna hi iska catch karne ka ek maatra tarika hai.
Step 2 — Rule + fix. Arithmetic ek wider unsigned intermediate mein karein taki koi wrap na ho sake — yahi woh uint32_t scaled; pattern hai jo parent note ka temperature function use karta hai.
uint32_t scaled = (uint32_t)adc * 1650U; /* 6 756 750 tak, 32 bits mein fit */
int16_t t = (int16_t)((int32_t)(scaled / 4095U) - 400);Yeh step kyun? Multiply se pehle uint32_t mein promote karna intermediate ko hold karne ki jagah deta hai, toh result sahi hota hai; unsigned rakhna multiply ko bhi MISRA-clean rakhta hai.
Step 3 — Dono boundary inputs evaluate karein (Cell Z).
adc = 0(min): → . ✓TEMP_MINse match karta hai.adc = 4095(max): , phir → . ✓TEMP_MAXse match karta hai.
Yeh step kyun? Dono ends test karna confirm karta hai ki formula exactly documented range produce karta hai aur kuch bhi usse bahar nahi spillta.
Step 4 — Verify karein ki result type fit karta hai. aur dono int16_t () mein fit hote hain.
Yeh step kyun? Intermediate safe hona kaafi nahi hai — final storage type bhi extreme outputs hold karne mein capable honi chahiye, warna aapne sirf overflow shift kiya hai.
Recall Reveal — Example 8 answer
4095 * 1650 ::: 6 756 750 — uint16_t (max 65535) overflow karta hai; uint32_t intermediate use karein
adc = 4095 deta hai ::: 1250 tenths = +125.0 °C; adc = 0 deta hai -400 tenths = -40.0 °C
Verify: ; endpoints aur dete hain; dono int16_t mein fit hain. Sab checked.
Example 9 — Cell I: exam-style "spot the category"
Step 1 — Teeno tiers yaad karein (Mandatory tier MISRA C:2012 Amendment 1 ke saath introduce hua tha; C:2004 mein sirf do tiers the).
| Tier | Deviation allowed? |
|---|---|
| Mandatory | Kabhi Nahi |
| Required | Sirf formal, documented deviation ke saath |
| Advisory | Recommended; deviations phir bhi record kiye jaane chahiye |
Yeh step kyun? Koi bhi single rule classify nahi kar sakte jab tak tiers ki poori ladder saamne na ho; "deviation allowed?" column hi woh hai jis par neeche har jawab turn karta hai.
Step 2 — (a) Rule 21.3 classify karein. Yeh Required hai. Reason: heap dangerous hai lekin justify karna hamesha impossible nahi, toh ek formal, documented deviation mil sakta hai (e.g. ek certified fixed-block pool allocator), though practice mein yeh shayad hi hota hai.
Yeh step kyun? "Required" woh tier hai jisme zyaadatar rules hote hain; yah recognise karna ki documented deviation yahan possible hai exactly woh hai jo ise Mandatory se alag karta hai.
Step 3 — (b) Rule 15.5 classify karein. MISRA C:2012 mein yeh Advisory hai. Reason: single-point-of-exit C:2004 ke Required Rule 14.7 se relax hokar Advisory ho gaya — multiple return statements ab allowed hain, single exit sirf recommend kiya jaata hai kyunki yeh cleanup centralize karta hai.
Yeh step kyun? Yeh classic exam trap hai: jo candidates C:2004 se seekhe hain woh "Required" answer dete hain aur mark kho dete hain. Version change dikhana misconception fix karta hai.
Step 4 — (c) Mandatory question ka jawab. Nahi — ek Mandatory rule kabhi bhi koi deviation nahi permit karta. Mandatory tier ke liye koi documented-deviation escape hatch nahi hai; yahi Required aur Mandatory ka poora distinction hai.
Yeh step kyun? Sawaal ek akele word "kabhi" par hinge karta hai; flat "nahi" state karna aur kyun (koi escape hatch exist nahi karta) complete, exam-ready answer hai.
Step 5 — Tiers practice mein kyun matter karte hain. Ek static analyser violations tier ke hisaab se report karta hai; aapki safety case (e.g. ISO 26262, DO-178C, ya IEC 62304) ko formally har Required deviation justify karna hoga aur koi Mandatory deviation carry nahi kar sakti. Dekho Formal Verification ke liye ki yeh justifications kaise discharge hoti hain.
Yeh step kyun? Classification ko real certification consequences se tie karna dikhata hai ki tiers trivial nahi hain — yeh drive karte hain ki aap legally kya ship kar sakte hain.
Recall Reveal — Example 9 answers
(a) Rule 21.3 ::: Required — documented deviation possible lekin rare (b) Rule 15.5 (single exit) ::: C:2012 mein Advisory (C:2004 mein Required Rule 14.7 tha) (c) Mandatory rule deviation ::: kabhi nahi allowed — koi escape hatch exist nahi karta
Verify: classification VERIFY mein truth check ki tarah encoded.
Recall Master self-test (sab answer karne ke baad reveal karein)
unsigned int u = -1; value on 32-bit ::: 0xFFFFFFFF = 4294967295 (Rule 10.3)
(uint8_t)300 ::: 44 (low 8 bits; Rule 10.3)
(-8) >> 1 kyun banned hai ::: signed/negative value ka right-shift implementation-defined hai (Rule 10.1)
int a[4] ke liye legal offsets ::: a+0 … a+4 (one-past-end); a+5 aur a-1 Rule 18.1 violate karte hain
malloc ban karne wala rule ::: Rule 21.3 (Required)
adc=4095 temperature in tenths ::: 1250 → +125.0 °C
C:2012 mein single-exit rule status ::: Advisory (Rule 15.5), C:2004 mein Required se relaxed