5.1.31 · D3 · Coding › C Programming › Compile-time assertions — static_assert
Aapne parent note padh liya hai aur ek-line rule jaante hain: static_assert(condition, "message") compiler ko build refuse karne par majboor kar deta hai agar condition compile time par zero evaluate ho. Yeh page theory ka ulta hai — yeh har tarah ke case grind karta hai jo yeh tool aap par throw kar sakta hai, taaki jab aap ise real mein dekho toh aap uska twin pehle hi dekh chuke ho.
Kisi bhi example se pehle: condition ek constant expression hai (dekho Constant expressions in C ). Matlab compiler iske liye ek fixed number v compute kar sakta hai — program run kiye bina . Phir:
Definition Kaun sa standard, kaun si spelling, kaun sa include?
C11 / C17 mein actual keyword ==_Static_assert== hai. Zyada friendly spelling static_assert ek macro hai jo <assert.h> header mein define hai — toh C mein static_assert likhne ke liye #include <assert.h> karna padta hai (ya raw _Static_assert use karo, jisme koi include nahi chahiye). Dekho C11 vs C23 language features .
C23 mein static_assert ek proper built-in keyword ban gaya aur message optional ho gayi.
C++ (C++11 se, message C++17 se optional) mein static_assert ek built-in keyword hai jisme koi include nahi chahiye — yeh confusion ka common source hai, kyunki C++ examples mein kabhi #include <assert.h> nahi hota.
Is page ke har C example mein file ke top par #include <assert.h> assume kiya gaya hai, jab tak _Static_assert directly use na ho.
static_assert ko ek machine samjho jo constant v khaati hai aur pass ya fail output deti hai. "Cases" woh sab shapes hain jo constant le sakti hai. Yeh har cell hai jo hum cover karenge.
Cell
Case class
Representative question
A
Truthy value (koi bhi non-zero → pass)
sizeof(int) == 4 ek 32-bit box par
B
Falsy value (exactly zero → fail)
wahi check ek 16-bit box par
C
Degenerate: bare non-zero literal
static_assert(1) — kya yeh pass bhi hota hai?
D
Zero-produce karne wala arithmetic
power-of-two mask x & (x-1)
E
Do cheezein sync mein rakhna
array length vs enum count
F
Boundary / limiting value
sabse chhoti legal size, x = 1, x = 0
G
Negative & signedness twist
-1 truthy hai; unsigned wrap
H
Real-world word problem
packet header ek fixed wire size mein fit hona chahiye
I
Exam twist
inme se kaun si constant expression nahi hai?
Neeche har example cell(s) ke saath tagged hai jise woh cover karta hai. Milke yeh har row ko touch karte hain.
Intuition Pehle yeh padho, phir har answer guess karo
Har example aapko compute karne se pehle Forecast karne ko kehta hai. Steps cover karo, decide karo "pass ya fail?", phir apne aap ko check karo. Yeh reflex build karne ka sabse fast tarika hai.
Worked example Example 1 — "true" kya count hota hai?
static_assert ( 1 , "never fails" );
static_assert ( 42 , "also never fails" );
static_assert ( sizeof ( char ), "char has non-zero size" );
Forecast: inme se kaun si teeno, agar koi ho, build fail karti hai?
Har ek ke liye value v dekho. Line 1: v = 1 . Line 2: v = 42 . Line 3: v = sizeof(char) = 1 (C standard sizeof(char) ko exactly 1 define karta hai — dekho sizeof operator ).
Yeh step kyun? Rule sirf "kya v zero hai ya nahi" care karta hai, toh pehle hum har condition ko uske number tak reduce karte hain.
Rule apply karo. 1 = 0 , 42 = 0 , 1 = 0 — sab non-zero.
Yeh step kyun? C mein "Truthy" ka matlab koi bhi non-zero value hai, sirf literally 1 nahi. Yeh degenerate cell C hai: ek bare number bhi legal condition hai.
Conclusion: teeno pass hoti hain aur koi code emit nahi karti.
Verify: v ∈ { 1 , 42 , 1 } , koi 0 ke barabar nahi, toh koi diagnostic trigger nahi karta. Runtime cost = 0 bytes.
Worked example Example 2 — ek check jo purposely fail hoti hai
#include <assert.h>
static_assert ( sizeof ( int ) == 4 , "This code assumes 32-bit int" );
Maano hum ek purane micro-controller par compile kar rahe hain jahan int 2 bytes hai.
Forecast: kya yeh build us micro-controller par succeed hogi?
sizeof(int) evaluate karo. Is platform par yeh 2 hai.
Yeh step kyun? sizeof compiler dwara target machine ke liye resolve hota hai, toh iska value compile time par fixed hota hai — ek valid constant.
Comparison evaluate karo. 2 == 4 false hai, aur C mein ek false comparison integer 0 produce karta hai. Toh v = 0 .
Yeh step kyun? C mein comparisons int values 1 (true) ya 0 (false) yield karte hain; hume woh number chahiye, English word nahi.
Rule apply karo. v = 0 ⇒ build fails , "This code assumes 32-bit int" print karta hai.
Verify: 32-bit box par wahi line 4 == 4 ⇒ v = 1 ⇒ pass deta hai. Same source, opposite outcome — poori tarah target se decide hota hai.
Neeche ka figure is exact machine ko walk karta hai. Ise left to right padho: blue box aapki condition hai; yellow box compiler hai jo ek fixed number v compute kar raha hai; diamond poochtha hai "kya v == 0 hai?". Green branch (jab v = 0 ho, jaise 32-bit sizeof(int)==4 jo v = 1 deta hai) build OK, emits no code tak jaati hai. Red branch (jab v = 0 ho, jaise 16-bit case jo v = 0 deta hai) build fails, print message tak jaati hai. Woh single diamond hi static_assert ka poora behaviour hai.
Classic "power of two hona chahiye" test mask x & ( x − 1 ) use karta hai, jahan & bitwise AND hai. Yeh raha kyun kaam karta hai, binary mein, isse assert karne se pehle.
x & ( x − 1 ) = 0 exactly powers of two ke liye kyun hota hai
Power of two mein exactly one bit set hoti hai: 8 = 100 0 2 . 1 ghataane se woh single 1 flip hokar 0 ho jaata hai aur uske neeche ki har bit 1 ban jaati hai: 7 = 011 1 2 . Unhe line up karo aur AND karo — koi column nahi hai jahan dono 1 hon, toh result 0 hai. Agar x mein do ya zyada bits set hain, toh 1 ghataane se higher bit untouched rehti hai, toh woh column AND hokar 1 deta hai aur result non-zero hota hai. Figure mein do rows dekho: top block (x = 8 ) all-zeros AND deta hai (green), bottom block (x = 12 ) ek shared 1 -bit rakhta hai (red).
Worked example Example 3 — power-of-two guard, boundary sweep
#define BUFFER_SIZE 1024 u /* unsigned constant */
static_assert ((BUFFER_SIZE & (BUFFER_SIZE - 1 )) == 0 ,
"BUFFER_SIZE must be a power of two" );
Forecast: BUFFER_SIZE = 1024 ke liye pass ya fail? 1000 ke liye? 1 ke liye? 0 ke liye?
Hum literal 1024u likhte hain toh BUFFER_SIZE ek unsigned constant hai. Yeh neeche ke degenerate case ke liye matter karta hai.
x = 1024 = 2 10 . Toh x − 1 = 1023 , aur 1024 & 1023 = 0 . Toh condition 0 == 0 hai, yaani v = 1 → pass .
Yeh step kyun? 1024 mein ek bit set hai (bit 10); mask use kill kar deta hai. Yeh intended good case hai.
x = 1000 (power of two nahi). 1000 = 111110100 0 2 mein kai bits set hain. 1000 & 999 = 992 = 0 . Condition 992 == 0 hai, yaani v = 0 → fail .
Yeh step kyun? Yeh cell D hai: arithmetic jo zero-or-not par land karta hai. Non-power-of-two mein ek bit survive karta hai, toh mask non-zero hoti hai aur assert use catch karta hai.
Boundary x = 1 = 2 0 . 1 & 0 = 0 → condition true → pass . 1 ek power of two hai .
Yeh step kyun? Cell F: sabse chhoti legal value false negative nahi honi chahiye. Yeh correctly pass hoti hai.
Degenerate x = 0 . Kyunki BUFFER_SIZE unsigned hai, 0u - 1 well-defined hai: yeh largest unsigned value tak wrap karta hai (sab bits 1 , jaise 32-bit unsigned ke liye 4294967295 ). Phir 0 & ( all ones ) = 0 → condition true → pass . Lekin 0 power of two nahi hai — trick mein zero par ek blind spot hai.
Yeh step kyun? Cell F phir se — degenerate input. Type matters hai : unsigned BUFFER_SIZE ke liye, 0 - 1 wrap karta hai aur fully defined hai; signed ke liye, 0 - 1 simply − 1 hai (koi underflow nahi, kyunki − 1 representable hai). Dono tarah 0 & anything == 0, toh mask akela 0 ko wrongly accept karta hai. BUFFER_SIZE > 0 add karo agar 0 possible hai.
Verify: 1024 & 1023 = 0 ; 1000 & 999 = 992 ; 1 & 0 = 0 ; 0 & ( 2 32 − 1 ) = 0 . Sirf 1000 case build fail karta hai — exactly wahi jo hum reject karna chahte the.
Common mistake Zero blind spot
(x & (x-1)) == 0 x = 0 ke liye true hai , phir bhi 0 power of two nahi hai. Agar config 0 ho sakti hai, toh x != 0 && (x & (x-1)) == 0 likho. Yeh bhoolna sabse common power-of-two bug hai.
Worked example Example 4 — array length vs enum count
#include <assert.h>
enum { COLOR_COUNT = 3 };
const char * names [] = { "red" , "green" , "blue" };
static_assert ( sizeof (names) / sizeof ( names [ 0 ]) == COLOR_COUNT,
"names[] and COLOR_COUNT are out of sync" );
Forecast: ek teammate names[] mein "cyan" add karta hai lekin COLOR_COUNT = 3 chhod deta hai. Build hogi ya break?
Array ko compile time par count karo. sizeof(names) array ke total bytes hain; sizeof(names[0]) ek element ke bytes hain. Unka ratio element count hai — ek constant, kyunki array sizes compile time par fixed hoti hain.
Yeh step kyun? Hume length ke liye compile-time number chahiye; yeh ratio standard idiom hai (dekho sizeof operator ). 4 strings ke saath ratio 4 hai.
Enum value padho. COLOR_COUNT = 3 — ek enumeration constant , jo ek constant expression hai .
Yeh step kyun? == ke dono sides compile-time constants hone chahiye static_assert ke liye.
Compare karo. 4 == 3 false hai → v = 0 → build fails sync message ke saath.
Yeh step kyun? Cell E: do independent definitions jo agree karni chahiye. Assert ek silent "array ke upar off-by-one" runtime bug ko loud compile error mein badal deta hai.
Verify: Edit se pehle, ratio = 3 , aur 3 == 3 ⇒ v = 1 ⇒ pass. Edit ke baad, ratio = 4 , aur 4 == 3 ⇒ v = 0 ⇒ fail. Guard exactly tab fire karta hai jab dono drift karte hain.
Worked example Example 5 — kya
− 1 true hai?
static_assert ( - 1 , "does this pass?" );
static_assert (( unsigned ) 0 - 1 > 0 , "unsigned wrap" );
Forecast: Line 1 negative number use karta hai. Line 2 0 se 1 ghataata hai. Kaun si build, agar koi ho, fail hoti hai?
Line 1: v = − 1 . Kya − 1 zero hai? Nahi. Rule hai "non-zero → pass", aur − 1 = 0 .
Yeh step kyun? Beginners expect karte hain ki sirf positive values "true" hoti hain, lekin C mein truthiness = 0 hai, toh negatives bhi true hote hain. Line 1 passes .
Line 2: (unsigned)0 - 1 evaluate karo. Unsigned arithmetic mein koi negative nahi hote; 0 se 1 ghataane par largest unsigned value par wrap karta hai (jaise 32-bit unsigned ke liye 4294967295 ). Woh ek bada positive number hai.
Yeh step kyun? Cell G ka twist: signed vs unsigned value change karta hai, aur static_assert sirf final value dekhta hai.
Compare karo. Kya woh bada number > 0 hai? Haan. Toh condition true hai, v = 1 → pass .
Yeh step kyun? Wrap comparison ko true banata hai — genuine surprise agar aapne socha "0 − 1 negative hai, toh > 0 false hoga". Signedness conclusion flip kar deta hai.
Verify: − 1 = 0 (truthy). ( 2 32 − 1 ) > 0 true hai. Dono lines pass karti hain. Moral: conclusion decide karne se pehle condition ko uske actual integer value C ke rules ke under tak reduce karo.
Worked example Example 6 — ek network packet header wire format mein fit hona chahiye
Ek protocol har header ko exactly 8 bytes fix karta hai. Aapki struct hai:
#include <assert.h>
#include <stdint.h>
struct Header {
uint16_t type; // 2 bytes
uint16_t length; // 2 bytes
uint32_t checksum; // 4 bytes
};
static_assert ( sizeof ( struct Header) == 8 , "Header must be exactly 8 bytes on the wire" );
Forecast: 2 + 2 + 4 = 8 . Kya yeh hamesha pass hoga?
Declared field sizes add karo. uint16_t = 2, uint16_t = 2, uint32_t = 4. Naive sum = 8 .
Yeh step kyun? Wire format 8 demand karta hai; hum check kar rahe hain ki compiler bhi agree karta hai.
Padding account karo. Compiler fields ke beech mein padding insert kar sakta hai unhe align karne ke liye. Yahan type aur length 2-byte aligned hain aur tightly pack hote hain; checksum 4-byte aligned hai aur already offset 4 par start hota hai — toh koi padding nahi chahiye aur sizeof(struct Header) = 8 .
Yeh step kyun? Struct size hamesha fields ka sum nahi hoti — padding use inflate kar sakti hai. Cell H ki realism exactly yahi hai: sizeof truth hai, aapka arithmetic nahi.
Rule apply karo. 8 == 8 ⇒ v = 1 ⇒ pass .
Yeh step kyun? Agar koi baad mein fields reorder kare ya type change kare taaki padding aaye (maano sizeof 12 ho jaaye), toh assert fail hoga, wire contract ko deployment se pehle protect karta hai.
Verify: 2 + 2 + 4 = 8 is layout par koi forced padding nahi ⇒ sizeof(struct Header) = 8 ⇒ 8 == 8 ⇒ pass. Isliye on-wire structs ke liye sizeof par static_assert standard guard hai.
Worked example Example 7 — illegal condition dhundho
int n = get_count ();
static_assert (n == 4 , "compile-time?" ); // (a)
static_assert ( sizeof (n) == 4 , "size of n" ); // (b)
#define K 4
static_assert (K == 4 , "macro constant" ); // (c)
static_assert ( get_count () == 4 , "call it" ); // (d)
Forecast: kaun si lines compile bhi hoti hain, aur kaun si outright errors hain?
Line (a): n == 4. n ek runtime variable hai (iska value get_count() se aata hai). Constant expression nahi → hard error : condition compile time par knowable nahi hai.
Yeh step kyun? Cell I "constant expression" ki boundary test karta hai. Runtime data forbidden hai — iske liye runtime `assert` hai.
Line (b): sizeof(n) == 4. sizeof(n) type ki size poochtha hai, n ki value nahi — compile time par resolve hota hai. Constant! 4-byte int ke liye, 4 == 4 ⇒ v = 1 ⇒ pass .
Yeh step kyun? Subtle twist: variable par sizeof static_assert mein legal hai kyunki sirf type matter karta hai, runtime value nahi.
Line (c): K == 4. K ek #define hai → preprocessor ise 4 se replace karta hai → 4 == 4 ek constant hai → v = 1 ⇒ pass .
Yeh step kyun? #defined numbers aur enum constants static_assert conditions ki bread-and-butter hain.
Line (d): get_count() == 4. Ek function call — iska result program run karke hi pata chalta hai. Constant nahi → hard error .
Yeh step kyun? Same lesson (a) se: kabhi bhi koi runtime evaluation allowed nahi hai.
Verify: Legal & passing: (b) aur (c). Hard errors (failed assertion bhi nahi — ek malformed condition): (a) aur (d). Dividing line hamesha yeh hai: kya compiler kuch run kiye bina ek fixed number compute kar sakta hai?
Recall Matrix cells vs examples (sab nine confirm)
A truthy value → Example 1 (1, 42, sizeof(char) sab non-zero)
B falsy value → Example 2 (sizeof(int)==4 16-bit box par v = 0 deta hai)
C bare non-zero literal → Example 1 (static_assert(1) passes)
D zero-producing arithmetic → Example 3 (power-of-two mask)
E do cheezein sync mein → Example 4 (array length vs COLOR_COUNT)
F boundary / degenerate → Example 3 (x = 1 aur x = 0 cases)
G negative & signedness twist → Example 5 (-1 truthy; unsigned wrap)
H real-world word problem → Example 6 (8-byte packet header)
I exam twist → Example 7 (kaun si condition constant expression nahi hai)
Sare nine cells A–I cover ho gaye — koi scenario nahi chhuta.
Mnemonic Universal reflex
"Ek number tak reduce karo, phir poochho: zero hai ya nahi?" Pass = non-zero. Fail = exactly zero. Illegal = constant hi nahi hai.
Kya static_assert(-1) pass hota hai? Haan — − 1 = 0 C mein truthy hai.
Kya (x & (x-1)) == 0 sahi se x = 0 reject karta hai? Nahi — yeh 0 ke liye true hai, toh x != 0 add karo.
Kya sizeof(n) == 4 static_assert mein valid hai even if n ek variable hai? Haan — sizeof type use karta hai, jo compile time par resolve hota hai.
get_count() == 4 static_assert mein illegal kyun hai?Function call ek runtime value hai, constant expression nahi.
1000 ke liye, 1000 & 999 kya hai? 992 (non-zero), toh power-of-two assert fail hoti hai.
C11/C17 mein static_assert spelling kaun sa header deta hai? <assert.h> (ya raw _Static_assert keyword use karo bina include ke).