5.5.19 · D5 · HinglishEmbedded Systems & Real-Time Software

Question bankMISRA C — rules for safety-critical C code

2,982 words14 min read↑ Read in English

5.5.19 · D5 · Coding › Embedded Systems & Real-Time Software › MISRA C — rules for safety-critical C code

Yeh page ek self-test hai. Har line apna answer ::: ke baad hide karti hai — try karo ki reveal karne se pehle out loud answer do. Goal rule numbers yaad karna nahi hai, balki un misconceptions ko pakadna hai jinhe rokne ke liye MISRA bana hai. Agar koi term unfamiliar lagey, pehle parent topic par wapas jao.


Reference card — shuru karne se pehle yeh padho

Traps ko answer karne se pehle, teen vocabularies clearly samajh lo: rule categories (ek rule kitna strict hai), essential type model (MISRA values ko kaise classify karta hai), aur is page par cite kiye gaye har rule ki ek one-line reminder. Inhe khula rakho.

Figure — MISRA C — rules for safety-critical C code
Figure — MISRA C — rules for safety-critical C code
Recall Is page par cite kiye gaye har rule ki one-line reminder

Rule 10.1 (Required) ::: Bitwise / shift operators essentially signed type ke operands par apply nahi kiye jaayenge. Rule 10.3 (Required) ::: Koi value essentially narrower ya different essential type category ke object ko assign nahi ki jaayegi. Rule 14.4 (Required) ::: if / while ka controlling expression essentially Boolean type ka hona chahiye. Rule 15.5 (Advisory) ::: Ek function ka end mein single point of exit hona chahiye. Rule 15.6 (Required) ::: Ek iteration/selection statement ka body ek compound statement (braces {} mein) hona chahiye. Rule 18.1 (Required) ::: Arithmetic se bana pointer original pointer ke usi array ke element ko address kare. Rule 18.7 (Required) ::: Kisi automatic (local) object ka address us object se zyada nahi jeena chahiye. Rule 21.3 (Required) ::: <stdlib.h> ke memory allocation functions (malloc/calloc/realloc/free) use nahi kiye jaayenge.


Teen sabse bade traps ko picture mein samajhna

Pointer, bitwise, aur shift traps source mein invisible hote hain — yeh sirf bit/memory level par bite karte hain. Yeh figures us level ko visible banati hain taaki neeche ke reveals ke paas kuch point karne ko ho.

Pointer arithmetic: valid vs invalid increments

Figure — MISRA C — rules for safety-critical C code

Figure dekho. Green arrows array a[4] ke andar rehte hain ya exactly one-past-the-end slot par land karte hain — Rule 18.1 ke under sab permitted. Red arrow &x se start hota hai, jo ek akela int hai jo array nahi hai; iske aage increment karna undefined memory mein land karta hai. "Spot the error" section mein yahi exact trap hai.

Signed vs unsigned bit patterns, aur mixing trap

Figure — MISRA C — rules for safety-critical C code

Top row wahi bit pattern do tareekon se padhna dikhata hai: ek signed int16_t ki tarah (orange mein sign bit use negative banata hai) aur ek unsigned uint16_t ki tarah (saare bits magnitude count karte hain). Bottom row Rule 10.3 mixing trap dikhata hai: ek negative int16_t ko uint32_t mein wide karna sign-extend karke ek bada positive number banata hai — silent, aur exactly isliye MISRA bit-level kaam unsigned bucket mein rakhta hai.

(-1) >> 1 implementation-defined kyun hai

Figure — MISRA C — rules for safety-critical C code

Kisi signed value ko right-shift karne ke do legal implementations hain. Figure dono dikhati hai: ek arithmetic shift sign bit ko andar copy karta hai (result -1 rehta hai), jabki ek logical shift mein 0 feed hota hai (result 0x7FFF... ban jaata hai). Kyunki C standard compiler ko dono mein se koi bhi choose karne deta hai, portable code signed right shift par rely nahi kar sakta — Rule 10.1 yahi hatata hai.


Sahi ya galat — justify karo

MISRA C safety-critical code mein pointers ka koi bhi use ban karta hai.
Galat. MISRA dangerous pointer uses ko constrain karta hai (unbounded arithmetic, dangling addresses) lekin pointers C mein essential hain aur freely allowed hain jab unka target provably valid ho — e.g. kisi fixed array mein pointer pass karna theek hai.
MISRA C:2012 ek function ko ek se zyada return statement rakhne se rokta hai.
Galat. Single-exit C:2012 mein sirf Advisory hai (Rule 15.5); multiple returns allowed hain. Yeh C:2004 (Rule 14.7) mein Required tha, jo is common misconception ka source hai.
Ek "Required" rule compliant code mein kabhi violate nahi ki ja sakti.
Galat. Ek Required rule se formal, documented deviation ke saath deviate kiya ja sakta hai. Sirf Mandatory rules (C:2012 Amendment 1 mein naye) kisi bhi deviation ki permission nahi dete.
MISRA C follow karna guarantee deta hai ki tumhara program bugs se free hai.
Galat. MISRA undefined/risky behaviour ke poore classes hatata hai, code ko zyada predictable aur verifiable banata hai — lekin logic errors, galat requirements, aur hardware faults bilkul possible rehti hain.
MISRA C ek compiler hai jo tumhara code check karta hai.
Galat. MISRA C ek set of guidelines hai (ek document). Checking alag Static Analysis Tools karte hain jo rules implement karte hain; MISRA khud kuch compile nahi karta.
Kyunki bitwise operators signed types par banned hain, MISRA effectively bit manipulation ban karta hai.
Galat. Bit manipulation allowed hai — sirf essentially unsigned operands par (essential-type figure dekho), jahaan bit pattern standardised hai. Rule 10.1 tumhe unsigned types use karne push karta hai, masks aur shifts abandon karne ko nahi.
Rule 21.3 (no malloc/free) isliye exist karta hai kyunki dynamic memory slow hai.
Mostly galat — speed minor hai. Asli reasons non-determinism hain: allocation runtime par fail ho sakti hai, heap ko unpredictably fragment kar sakti hai, aur variable timing introduce kar sakti hai, yeh sab hard real-time ya life-critical path mein unacceptable hain.
Rule number MISRA C:2004 aur C:2012 mein same hoti hai.
Galat. Numbering reorganise ki gayi thi. E.g. single-exit C:2004 Rule 14.7 se C:2012 Rule 15.5 par chali gayi, aur signed-shift C:2004 Rule 12.7 se C:2012 Rule 10.1 par.
Static allocation stack allocation se hamesha safer hai MISRA ke under.
Zaruri nahi. Dono heap se bachte hain aur acceptable hain; requirement hai compile-time-known bounds, yeh nahi ki ek storage class doosre se better hai. Ek bada static array RAM waste kar sakta hai jaise deep recursion stack overflow kar sakta hai.
Ek Advisory rule ko silently ignore kiya ja sakta hai bina kisi record ke.
Ideally nahi. Advisory ka matlab hai "jahaan reasonable ho wahaan follow karo", lekin departures phir bhi record honi chahiye — strictness sirf review ki differ karti hai Required deviation se, documentation ki habit ki nahi.

Error dhundo

int *p = &x; p++; jahaan x ek single int hai — kya toot ta hai?
p++ ab ek akele object ke past memory address karta hai (Rule 18.1 — arithmetic usi array ke andar rehni chahiye). Kyunki x array nahi hai (pointer figure mein red arrow dekho), p undefined memory point karta hai.
unsigned int u = -1; — yeh MISRA violation kyun hai jabki yeh "kaam karta" hai?
Yeh ek implicit signed-to-unsigned conversion (Rule 10.3) par rely karta hai jo silently 0xFFFFFFFF mein wrap ho jaata hai. Ek reviewer intended value nahi dekh sakta; MISRA intent explicit chahta hai, e.g. 0xFFFFFFFFU.
if (cond)
    action1();
    action2();   /* trap: NOT inside the if */
Author ne kya socha hota hai ki kya hoga, versus actually kya hota hai?
Woh sochte hain ki dono calls if ke andar hain. Actually sirf action1() guarded hai; action2() hamesha run karta hai. Rule 15.6 braces require karta hai taaki block unambiguous ho — corrected form neeche dekho.
Upar ke liye braced fix — Rule 15.6 ambiguity kaise hatata hai?
Intended body ko {} mein wrap karna boundary explicit banata hai, isliye ek stray second statement ab nested nahi lag sakta:
if (cond) {
    action1();
}
action2();       /* obviously always runs */
int* get_local(void){ int local=10; return &local; } — kya galat hai?
local ki automatic storage hoti hai aur function return hone par destroy ho jaati hai, isliye returned address ek dangling pointer hai (Rule 18.7). Baad mein ise dereference karna undefined behaviour hai.
int mask = 0xFF; int result = value & mask; — kaunsa rule aur kyun?
Rule 10.1 — bitwise & essentially signed operands par apply ho raha hai. Signed types par sign bit kuch bit-level operations ko implementation-defined banata hai, isliye dono operands unsigned hone chahiye.
(-1) >> 1 — yeh dangerous kyun hai?
Result implementation-defined hai: compiler arithmetic shift kar sakta hai (result -1 deta hai) ya logical shift (result 0x7FFFFFFF deta hai) — shift figure mein dono branches. Portable code dono mein se kisi par bhi rely nahi kar sakta, isliye signed values shift karna Rule 10.1 se disallowed hai.
Ek reviewer kehta hai "yeh function compliant hai, iska ek return hai aur koi malloc nahi." Kya yeh compliance conclude karne ke liye kaafi hai?
Nahi. Compliance ka matlab hai sabhi applicable rules satisfy karna (type model, pointer rules, control flow, prototypes, linkage, etc.), har ek verified — do green rules spot karna baaki ke baare mein kuch prove nahi karta.

Refactor walkthrough — faulty function se compliant tak

Har numbered stage ko neeche ek MISRA check ki tarah padho jo sequence mein apply ho raha hai, broken version ko compliant mein badal raha hai.

Faulty starting point — tum kitne distinct violations dhund sakte ho?

int* scale(int adc) {
    int *buf = malloc(4);         /* A */
    if (adc)                      /* B */
        buf[0] = adc & 0xFF;      /* C */
    return buf;                   /* D */
}

::: Chaar: (A) malloc Rule 21.3 todta hai; (B) if controlling expression int hai, Boolean nahi (Rule 14.4), aur body mein braces nahi hain (Rule 15.6); (C) signed operands par bitwise & (Rule 10.1); (D) heap memory return karna jise caller ko free karna hoga — woh poora dynamic-memory pattern jo Rule 21.3 forbid karta hai.

Stage 1 — Rule 21.3 fix karo: malloc ki jagah kya aata hai?
Fixed, compile-time-bounded storage use karo jo caller pass kare, taaki koi heap nahi aur koi ownership question nahi: void scale(uint16_t adc, uint16_t *buf).
Stage 2 — Rules 14.4 aur 10.1 fix karo: condition aur mask kaise likhni chahiye?
Condition ko genuine Boolean comparison banao aur mask unsigned bucket mein rakho: if (adc > 0U) aur buf[0] = adc & 0xFFU;.
Stage 3 — Rule 15.6 fix karo: body ko kya wrap karta hai?
if body ke around braces, jo final compliant form deta hai:
static void scale(uint16_t adc, uint16_t *buf) {
    if (adc > 0U) {
        buf[0] = adc & 0xFFU;
    }
}

Why questions

MISRA unsigned target ke liye -1 ki jagah 0xFFFFFFFFU jaise explicit constants kyun prefer karta hai?
Kyunki yeh programmer ka intent source mein visible banata hai. Ek explicit value review mein survive karti hai aur portable hai, jabki ek implicit conversion dono — asli value aur yeh fact ki conversion hui — chhupa deta hai.
Single-exit ab bhi recommended kyun hai jabki C:2012 ne ise Advisory kar diya?
n return statements ke saath n tak aisi jagahein hain jahaan cleanup (mutexes unlock karna, files band karna, resources free karna) bhool sakta hai. Ek exit point cleanup ko ek verifiable jagah par concentrate karta hai, review aur Formal Verification asaan karta hai.
MISRA un rules par itna zyada kyun lean karta hai jo tool automatically check kar sake?
Manual review scale par slow aur error-prone hai. Decidable rules Static Analysis Tools ko poore codebase mein consistently enforce karne dete hain, taaki human effort un kuch undecidable rules par focus ho sake jinhein judgement chahiye.
C ke built-in types use karne ki jagah essential type model kyun introduce kiya?
C ke usual arithmetic conversions silently types promote aur mix karte hain (signed/unsigned/char/float), data lose karte hain ya signedness flip karte hain — bit-pattern figure mein sign-extension trap. Essential type model MISRA ko ek precise vocabulary deta hai risky mixes forbid karne ke liye jabki safe ones allow karta rahe.
MISRA ISO 26262, DO-178C, aur IEC 62304 jaise standards ke liye especially relevant kyun hai?
Woh functional-safety standards predictable, verifiable code ka evidence demand karte hain. Ek MISRA-compliant C subset ek recognized, tool-checkable coding standard provide karta hai jo woh evidence produce karne mein help karta hai.
Heap ban karna RTOS par real-time behaviour kyun improve karta hai?
Heap allocation ka execution time variable hota hai, hard-to-bound hota hai, aur unpredictably fail ho sakta hai. Ise hatane se worst-case execution time analysable aur memory usage fixed ho jaata hai — dono deadlines meet karne ke prerequisites hain.
Mandatory category exist kyun karta hai agar Required ko todne ke liye pehle se sign-off chahiye?
Kyunki kuch rules itni severe errors guard karte hain (ya itne clearly always-wrong hain) ki koi bhi justification violation ko safe nahi bana sakti — isliye MISRA deviation option hi hata deta hai.

Edge cases

Kya p + 1 jahaan p array ke last element ko point karta hai, Rule 18.1 ki violation hai?
Nahi. C explicitly ek one-past-the-end pointer exist karne deta hai (sirf dereference nahi hona chahiye) — pointer figure mein sabse right waala green arrow. Rule 18.1 arithmetic ko array ke andar ya exactly ek past end par land karne deta hai.
Ek single scalar ka pointer jo array element nahi hai, usmein arithmetic ke baare mein kya?
Arithmetic ke liye length-one array ki tarah treat kiya jaata hai. Isliye &x + 1 (one-past) technically valid hai, lekin &x + 2 ya &x + 1 dereference karna undefined hai — safe rule of thumb yeh hai: akele objects ke pointers par arithmetic mat karo.
Ek empty if body if (cond); likha gaya — kya Rule 15.6 phir bhi apply hota hai, aur kya yeh sensible bhi hai?
Haan apply hota hai — controlling statement empty ; hai, jo khud ek trap hai (yeh if kuch nahi karta). Rule 15.6 tumhe if (cond) { } likhne push karta hai taaki intentionally empty body explicit ho aur accidental stray ; visible ho.
Zero-length ya degenerate loops jaise for(;;) — kya yeh forbidden hain, aur ek intentional infinite loop kaise likha jaana chahiye?
Deliberate infinite loop theek hai (RTOS tasks inhe use karte hain), lekin Rule 15.6 phir bhi ek braced compound body require karta hai, aur intent unmistakable hona chahiye. while (1) se better for (;;) { /* task body */ } prefer karo, aur empty case ko explicit body do:
for (;;) {
    do_task();        /* clearly the task loop */
}
while(0) statement; — yahan trap kya hai?
Body kabhi execute nahi hoti, isliye yeh almost hamesha mistake ya dead code hai; aur braces ke bina (Rule 15.6) yeh easily misread hota hai ki loop kaun sa statement control karta hai. Body ko {} ke saath explicit banao aur question karo ki yeh loop exist kyun karta hai.
Kya tum MISRA ke under ek pointer free() kar sakte ho agar memory kabhi malloc se nahi aayi?
Question meaningless hai — Rule 21.3 allocation functions ke saath free() ko bhi ban karta hai. In <stdlib.h> memory functions ka koi compliant use hota hi nahi.
Agar overflow se bachne ke liye int16_t mein store karne se pehle ek wider intermediate (e.g. uint32_t scaled;) use kiya jaaye, toh kya yeh MISRA problem hai?
Nahi — ek wider unsigned intermediate use karna exactly recommended pattern hai. int16_t mein final narrowing explicit honi chahiye aur bounds-checked honi chahiye (Rule 10.3) taaki koi silent truncation na ho.

Recall Quick self-grade

Agar tumne koi bhi answer sirf "true/false" ya "yes/no" mein bina reason ke diya, toh use revisit karo — reasoning hi woh hai jo MISRA compliance reviews actually demand karte hain. One-word answers misconceptions chhupate hain ::: Real understanding mechanism explain karne se dikha hai (dangling storage, implicit conversion, implementation-defined behaviour), verdict se nahi.