Exercises — MISRA C — rules for safety-critical C code
5.5.19 · D4· Coding › Embedded Systems & Real-Time Software › MISRA C — rules for safety-critical C code
Yeh page MISRA C parent note ke liye ek self-test ladder hai. Har problem ko solution kholne se pehle solve karo. Levels "rule pehchano" se shuru hokar "compliant module design karo" tak jaati hain. Sab kuch MISRA C:2012 follow karta hai jab tak koi problem kuch aur na kahe.
Prerequisites ek nazar mein: Memory Safety, Embedded C Best Practices, Static Analysis Tools.
Level 1 — Recognition
"Kya main rule ka naam le sakta hoon aur violation dekh sakta hoon?"
Exercise 1.1 — Banned call dhundo
Batao ki yeh line MISRA-compliant hai ya nahi. Rule ka naam batao.
int *buf = malloc(64 * sizeof(int));Recall Solution
Compliant nahi hai. Yeh Rule 21.3 (Required) violate karta hai — <stdlib.h> ke memory allocation/deallocation functions (malloc, calloc, realloc, free) use nahi hone chahiye.
Kyun banned hai: dynamic allocation runtime par fail ho sakta hai, heap ko unpredictably fragment kar sakta hai, aur timing non-determinism inject kar sakta hai — yeh sab ek hard real-time system mein fatal hain (dekho Real-Time Operating Systems (RTOS)).
Fix: compile-time bound ke saath static allocation karo.
#define BUF_LEN 64
int buf[BUF_LEN];Exercise 1.2 — Signed bit-twiddling
Yeh kaun sa rule violate karta hai?
int flags = 0xF0;
int high = flags >> 4;Recall Solution
Rule 10.1 (Required) violate hota hai — bitwise/shift operators essentially signed operands par apply nahi hone chahiye.
Kyun: ek signed value ko right-shift karna implementation-defined hai (compiler top bit ko sign bit se ya zeros se fill kar sakta hai). Negative int ka bit pattern two's-complement details par depend karta hai jis par kabhi rely nahi karna chahiye.
Fix: operands ko unsigned banao.
unsigned int flags = 0xF0U;
unsigned int high = flags >> 4; /* logical shift, defined */Exercise 1.3 — Missing braces
Kya yeh compliant hai, aur kya trap hai?
if (ready)
arm();
fire();Recall Solution
Compliant nahi hai. Rule 15.6 (Required) violate hota hai — selection/iteration statement ki body ek compound statement ({ }) honi chahiye.
Trap: indentation jhooth bol rahi hai. Sirf arm() if se guarded hai; fire() unconditionally chalti hai. Yeh classic dangling-body bug hai.
Fix:
if (ready) {
arm();
}
fire(); /* now visibly unconditional */Level 2 — Application
"Kya main code ko compliant banana ke liye rewrite kar sakta hoon?"
Exercise 2.1 — Implicit conversion hatao
Ise rewrite karo taaki intent explicit aur MISRA-compliant ho. u mein kya value jaati hai?
unsigned int u = -1;Recall Solution
Yeh write implicit signed→unsigned conversion par rely karta hai jo silently wrap karta hai. 32-bit unsigned int par, ban jaata hai .
Yeh essential type model (Rule 10.3, Required) ko offend karta hai kyunki tum ek unsigned object ko signed constant assign kar rahe ho.
Fix — intended pattern directly likho:
unsigned int u = 0xFFFFFFFFU; /* value visible, no hidden wrap */Ab deliberately hai, aur reviewer intent dekh sakta hai.
Exercise 2.2 — Dangling pointer return
Yeh function compile hota hai lekin ek landmine hai. Rule batao aur fix karo.
int *get_reading(void) {
int reading = 42;
return &reading;
}Recall Solution
Rule 18.7 (Required) violate hota hai — automatic storage wale object ka address kisi aisi cheez mein copy nahi hona chahiye jo use outlive kare. (Historical MISRA C:2004 Rule 17.6.)
Kyun fatal hai: reading stack par rehta hai. Jaise hi get_reading return karta hai, woh stack slot free ho jaata hai reuse ke liye; returned pointer dangle karta hai. Dekho Memory Safety.
Fix — value se return karo:
int get_reading(void) {
int reading = 42;
return reading; /* a copy travels out, no address escapes */
}Exercise 2.3 — Pointer array se bahar chala jaata hai
Violation explain karo aur data[0..n-1] sum karne ke liye ek compliant loop banao.
int data[4] = {1, 2, 3, 4};
int *p = &data[0];
int s = 0;
for (int i = 0; i <= 4; i++) { /* note the <= */
s += *p;
p++;
}Recall Solution
Do problems hain, dono Rule 18.1 (Required) se — arithmetic se produce hone wala pointer same array ke element ko address karta rehna chahiye (one-past-the-end hold karna allowed hai lekin dereference karna nahi).
Aakhri iteration par (i == 4) loop *p dereference karta hai jab p one past the end point karta hai — undefined behaviour aur garbage read.
Fix: < n use karo, aur agar loop simple rakho toh raw pointer ki zaroorat hi nahi.
int data[4] = {1, 2, 3, 4};
int s = 0;
for (int i = 0; i < 4; i++) { /* strictly less-than */
s += data[i];
}
/* s == 10 */Correct sum: .
Level 3 — Analysis
"Kya main correctness, overflow, aur edge cases ke baare mein reason kar sakta hoon?"
Exercise 3.1 — Intermediate mein overflow
Ek 8-bit ADC adc ko [0, 255] mein return karta hai. Hum celsius = (adc * 130) / 255 - 40 integer types se compute karna chahte hain. Ek junior likhta hai (assume karo file ke top par #include <stdint.h> hai, jo uint8_t/int8_t deta hai):
#include <stdint.h>
uint8_t adc; /* 0..255 */
int8_t celsius;
celsius = (adc * 130) / 255 - 40;adc = 255 ke liye arithmetic trace karo aur decide karo ki intermediate adc * 130 safe hai ya nahi. Kaun se compliant types ise fix karte hain, aur adc = 255 aur adc = 0 par celsius kya hai?
Recall Solution
Promotion analysis. adc * 130 mein, adc (ek uint8_t) int mein promote hota hai aur 130 int hai, toh product int mein compute hota hai. adc = 255 ke liye: , kya yeh 16-bit int ke guaranteed range mein fit hota hai? Nahi — C standard sirf guarantee karta hai ki int hold kare, aur . 16-bit target par yeh signed int overflow → undefined behaviour hai. Yahi essential-type danger hai jiski MISRA warning deta hai: int ki width par rely karna.
Fix: ek wide unsigned intermediate force karo taaki math defined aur portable ho. Neeche ke uint32_t/int16_t types bhi <stdint.h> se aate hain.
#include <stdint.h>
uint8_t adc;
int16_t celsius;
uint32_t scaled = ((uint32_t)adc * 130U); /* defined, wide */
celsius = (int16_t)(scaled / 255U) - 40;Values:
adc = 255: .adc = 0: . Dono[-40, 125]mein hain, tohint16_tinhe safely hold karta hai.
Exercise 3.2 — Kitne cleanup paths hain?
Ek function mutex acquire karta hai, phir inhe exits hain (MISRA C:2012 multiple returns allow karta hai, Rule 15.5 Advisory hai):
int handle(int x) {
lock(&m);
if (x < 0) return -1; /* exit A */
if (x == 0) return 0; /* exit B */
if (x > 100) return -2; /* exit C */
unlock(&m);
return x; /* exit D */
}Parent note ke counting argument se, kitne exit paths hain, kitne par mutex correctly release hota hai, aur kitne bugs hain?
Recall Solution
exit paths hain (A, B, C, D). unlock sirf exit D se pehle hai.
- Exits A, B, C lock hold karte hue return karte hain → 3 buggy paths (next entry par deadlock).
- Exit D → 1 correct path. Exactly isliye Rule 15.5 (Advisory) single exit recommend karta hai: returns ke saath tumhare paas up to jagah hain jahan cleanup bhool sakti hai. Ek exit par collapse karne se unlock ek jagah hota hai. Single-exit fix:
int handle(int x) {
int result;
lock(&m);
if (x < 0) { result = -1; }
else if (x == 0) { result = 0; }
else if (x > 100) { result = -2; }
else { result = x; }
unlock(&m); /* one guaranteed release */
return result;
}Original mein buggy paths: 3.
Neeche diya figure un chaar exit paths ko side by side trace karta hai. Har red box ko ek decision node se upar follow karo: unme se teen (A, B, C) function ko mutex locked rakhte hue chhod dete hain — yeh woh deadlock hai jo hone ka intezaar kar raha hai. Sirf green path (D) single unlock tak pahunchta hai. Yeh visual argument hai ki Rule 15.5 ek exit kyun prefer karta hai: ek release, verify karne ki ek jagah.

Exercise 3.3 — Signed shift outcome
Ek compiler signed types ke liye arithmetic right shift document karta hai. (-1) >> 1 ke compilers mein kya do possible documented results hain, aur kaun sa MISRA rule ensure karta hai ki tum dono mein se kisi par depend na karo?
Recall Solution
Two's complement mein -1 all-ones hai (0xFFFF...).
- Arithmetic shift (sign-preserving): top bit sign se fill hota hai → all-ones rehta hai → result
-1. - Logical shift (zero-fill): top bit 0 ban jaata hai →
0x7FFF...→ 32-bit par result . Kyunki choice implementation-defined hai, Rule 10.1 essentially-signed operands ko shift karna bilkul forbid karta hai. Portable code sirfunsignedshift karta hai.
Level 4 — Synthesis
"Kya main kaafi rules ko ek sahi module mein combine kar sakta hoon?"
Exercise 4.1 — Compliant bounded lookup
Ek function lookup(idx) design karo jo 8 uint16_t values ki fixed table se table[idx] return kare, aur kisi bhi out-of-range index ke liye sentinel 0xFFFFU return kare. Honor karo: no dynamic memory (21.3), Boolean controlling expression (14.4), har jagah braces (15.6), single exit (15.5, advisory), aur koi signed bit tricks nahi. Ise likho aur har choice justify karo.
Recall Solution
#include <stdint.h>
#define TABLE_LEN (8U)
#define SENTINEL (0xFFFFU)
static uint16_t lookup(uint16_t idx); /* Rule 8.2 prototype form */
static uint16_t lookup(uint16_t idx) {
static const uint16_t table[TABLE_LEN] =
{ 10U, 20U, 30U, 40U, 50U, 60U, 70U, 80U };
uint16_t result;
if (idx < TABLE_LEN) { /* 14.4: genuine Boolean compare */
result = table[idx]; /* 18.1: idx proven in-range */
} else { /* 15.6: braces on both arms */
result = SENTINEL;
}
return result; /* 15.5: single exit */
}Justifications:
static consttable → koimallocnahi (21.3), flash mein rehta hai, compile-time bounded.idx < TABLE_LENek comparison hai → essentially Boolean, 14.4 satisfy karta hai; yeh subsequent index ko 18.1 ke liye valid bhi prove karta hai.- Har branch braced (15.6); end mein ek
return(15.5). - Sab arithmetic
unsignedmein — koi signed shift/mask concerns nahi (10.1). uint16_t<stdint.h>se aata hai (top par included). Test values:lookup(0) == 10,lookup(7) == 80,lookup(8) == 0xFFFF (=65535),lookup(100) == 65535.
Exercise 4.2 — Deviation ya redesign?
Tumhara legacy driver internally malloc use karne wala vendor hal_alloc() zaroor call karta hai. Tumhara target ISO 26262 ASIL-D se certify hota hai. MISRA classification system ke under, tumhare paas kya do legitimate options hain aur 21.3 kaun si category hai?
Recall Solution
Rule 21.3 Required hai (Mandatory nahi), toh deviation possible hai lekin free nahi.
Option 1 — Formal documented deviation. Kyunki Required rules sirf formal, recorded deviation ke saath deviation allow karte hain, tumhe: rationale document karna hoga, safety analysis dikhani hogi ki residual risk controlled hai (jaise sabhi hal_alloc calls sirf init par hote hain, hard-real-time loop mein kabhi nahi), ise review/approve karwana hoga, aur log karna hoga. Yeh ISO 26262 / DO-178C-style paper trail hai.
Option 2 — Redesign. hal_alloc ko startup par allocate hone wale static pool se replace karo, violation ko completely eliminate karo. ASIL-D ke liye preferred hai kyunki hazard ko argue karne ki jagah remove karta hai.
Note: agar 21.3 Mandatory hota (C:2012 mein new category), toh koi deviation allowed nahi hota aur sirf Option 2 rehta.
Level 5 — Mastery
"Kya main unfamiliar code audit kar sakta hoon aur har judgement defend kar sakta hoon?"
Exercise 5.1 — Full audit
Is function ke har MISRA C:2012 violation ki list banao aur fix karo. Har ek ke liye rule number batao. (Assume karo surrounding file mein already #include <stdint.h> hai, jo uint8_t/int8_t supply karta hai.)
#include <stdint.h>
int8_t *scale(uint8_t *src, int n) {
int8_t out[8];
for (int i = 0; i <= n; i++) {
int v = src[i] << 1;
if (v > 127)
out[i] = 127;
else
out[i] = v;
}
return out;
}Recall Solution
Violations found (with rule numbers):
outreturn karna → Rule 18.7 (Required).outmein automatic storage hai (ek stack array);scalereturn karte hi returned pointer dangle karta hai.i <= noff-by-one → Rule 18.1 (Required) + Rule 14.2 loop discipline. Aakhri iteration par yehsrc[n]read karta hai aurout[n]write karta hai, dono intended range se ek element aage; aur bura,outke paas sirf 8 slots hain, toh koi bhin >= 8stack buffer overrun kar dega (dekho Memory Safety).src[i] << 1signed context mein → Rule 10.1 (Required).src[i]shift se pehleint(signed) mein promote hota hai, toh ek essentially-signed operand par shift apply hota hai — implementation-defined aur banned.ifaurelsebodies par missing braces → Rule 15.6 (Required).- Implicit narrowing
out[i] = v(int→int8_t) → Rule 10.3 (Required), essential-type model. Assignment silently ek wide signed type ko kisi explicit cast ke binaint8_tmein narrow karta hai. - Multiple exit / structure concerns → Rule 15.5 (Advisory). Original end mein ek pointer return karta hai; neeche ka redesign
voidreturn karta hai aur caller-owned buffer ke through write karta hai, dangling-pointer class ko completely sidestep karta hai.
Compliant rewrite — caller output buffer ka maalik hai (koi dangling pointer nahi, toh 18.7 satisfied hai); bounded loop buffer tak clamped (18.1); unsigned math (10.1); har body par braces (15.6); explicit narrowing cast (10.3); single exit (15.5):
#include <stdint.h>
#define OUT_LEN (8U)
static void scale(const uint8_t *src, uint16_t n, int8_t *out);
static void scale(const uint8_t *src, uint16_t n, int8_t *out) {
uint16_t i;
uint16_t count = (n < OUT_LEN) ? n : OUT_LEN; /* clamp to buffer */
for (i = 0U; i < count; i++) { /* strict <, bounded */
uint16_t v = ((uint16_t)src[i]) << 1U; /* unsigned shift */
if (v > 127U) {
out[i] = 127;
} else {
out[i] = (int8_t)v; /* explicit narrow */
}
}
}Worked value check: src[i] = 100 → v = 200, clamped to 127. src[i] = 40 → v = 80, stays 80. src[i] = 0 → 0.
Exercise 5.2 — Saturation table
Upar wale compliant scale se, src = {10, 60, 100, 128}, n = 4. Output array do.
Recall Solution
Har ek ko double karo, phir 127 par saturate karo:
- 127 (clamped)
- 127 (clamped)
Output = {20, 120, 127, 127}.
Neeche diya saturation curve clamp ko visually dikhata hai: violet line hai. Yeh slope 2 par climb karta hai jab tak input ke knee tak nahi pahunchta, phir orange ceiling ke against flat ho jaata hai. Magenta dots hamare chaar sample inputs hain — aakhri do ceiling par hain kyunki unke doubled values ( aur ) dono se zyada hain.

Recall Self-test summary
Rule 21.3 bans ::: dynamic memory (malloc/free from <stdlib.h>)
Rule 18.1 requires ::: pointer arithmetic same array ke andar rahti hai
Rule 18.7 prevents ::: automatic (stack) storage ka address return/store karna
Rule 10.1 forbids ::: essentially signed operands par bitwise/shift operators
Rule 10.3 forbids ::: explicit cast ke bina incompatible essential types mein assignment
Rule 15.6 requires ::: har if/else/loop body ke around braces
Rule 15.5 (Advisory) recommends ::: function mein ek single exit point
Exact-width types uint8_t/int16_t/etc. aate hain ::: standard header <stdint.h> se
C:2012 ki teen categories hain ::: Mandatory, Required, Advisory