5.1.31 · D4 · HinglishC Programming

ExercisesCompile-time assertions — static_assert

2,513 words11 min read↑ Read in English

5.1.31 · D4 · Coding › C Programming › Compile-time assertions — static_assert

Shuru karne se pehle, ek shared vocabulary reminder taaki koi symbol bina explanation ke use na ho:


Level 1 — Recognition

Exercise 1.1

Har snippet ke liye yes/no answer do: kya condition ek constant expression hai jo static_assert accept karega?

static_assert(sizeof(long) >= 4, "a");        // (A)
int k = 8; static_assert(k == 8, "b");         // (B)
static_assert(3 + 4 == 7, "c");                // (C)
static_assert(getchar() != 0, "d");            // (D)
Recall Solution
  • (A) YES. sizeof(long) compiler ke dwara fix hota hai → constant.
  • (B) NO. k ek runtime variable hai; bhale hi is line par uski value 8 ho, language sirf compile-time constants allow karti hai. k ke liye plain assert use karo.
  • (C) YES. Sab literals hain; compiler 7 == 71 compute karta hai.
  • (D) NO. getchar() ek function call hai — yeh value tabhi produce kar sakta hai jab program chal raha ho.

Exercise 1.2

C11 mein static_assert ki friendly spelling likhne ke liye kaun sa header #include karna zaroori hai? Aur agar kuch include na karo toh raw keyword kya hai?

Recall Solution
  • Header: <assert.h> (dekho Header <assert.h>).
  • Raw C11 keyword: _Static_assert. C23 mein static_assert khud ek keyword hai aur kisi header ki zaroorat nahi (dekho C11 vs C23 language features).

Level 2 — Application

Exercise 2.1

Ek algorithm 8 flags ko ek byte mein pack karta hai. Ek static_assert likho (C11-style, message ke saath) jo guarantee kare ki char exactly 8 bits ka hai, uski byte count check karke. (CHAR_BIT use karo <limits.h> se.)

Recall Solution
#include <assert.h>
#include <limits.h>
static_assert(CHAR_BIT == 8, "This packing assumes 8-bit bytes");

CHAR_BIT ek #defined constant hai → poori condition constant hai. Kisi bhi (rare) machine par jahan CHAR_BIT != 8 ho, build ruk jaata hai bit-packing bug ship hone se pehle.

Exercise 2.2

Aapke paas ek lookup table hai aur ek matching count. static_assert fill karo taaki dono sync mein rahe:

enum { STATE_COUNT = 4 };
const char *state_name[] = { "idle", "run", "pause", "stop" };
static_assert( /* ??? */ , "state_name[] out of sync with STATE_COUNT");
Recall Solution
static_assert(sizeof(state_name)/sizeof(state_name[0]) == STATE_COUNT,
              "state_name[] out of sync with STATE_COUNT");

sizeof(state_name) = array ke total bytes; sizeof(state_name[0]) = ek element (ek pointer) ke bytes. Unka ratio hai elements ki sankhya, jo compile time par pata hoti hai. Abhi yeh 4 == STATE_COUNT → pass karta hai. Ek 5th name add karo bina enum bump kiye aur yeh 5 == 4 → build fail ho jaata hai. Dekho sizeof operator aur Enumerations (enum).

Exercise 2.3

Ek static_assert likho ki #defined ALIGNMENT of 16 ek positive power of two hai.

Recall Solution
#define ALIGNMENT 16
static_assert(ALIGNMENT > 0 && (ALIGNMENT & (ALIGNMENT - 1)) == 0,
              "ALIGNMENT must be a positive power of two");

Trick x & (x-1) x ka lowest set bit clear karta hai; power of two ke liye exactly ek set bit hota hai, isliye result 0 hota hai. 16 (10000₂) ke liye, 16 & 15 = 10000₂ & 01111₂ = 0 → pass. Extra ALIGNMENT > 0 degenerate case 0 se bachata hai, jisme 0 & -1 == 0 galat taur par pass ho jaata.


Level 3 — Analysis

Exercise 3.1

Yeh code aapke laptop par compile hota hai lekin ek teammate build error report karta hai. Exactly batao kaunsi line fail ho rahi hai aur kyun, aur isse portable kaise banayein.

#include <assert.h>
static_assert(sizeof(void*) == 8, "need 64-bit pointers");
Recall Solution

Koi syntactically galat nahi hai. Failure semantic hai: 32-bit target par sizeof(void*) 4 hota hai, isliye 4 == 80 → build "need 64-bit pointers" ke saath ruk jaata hai. Yahi static_assert ka kaam hai — usne ek galat assumption teammate ki machine par build time par pakad li. Isse portable banane ke liye, ya toh dono widths ko #if/alag code paths se handle karo, ya assertion ko relax karo jo code ko sach mein chahiye (jaise sizeof(void*) >= 4).

Exercise 3.2

Har ek ke liye result predict karo (pass / fail, aur compiler jo numeric value compute karta hai):

static_assert(sizeof(char) == 1, "P");            // (A)
static_assert(-1 < 0u, "Q");                       // (B)  tricky!
static_assert((1 << 4) == 16, "R");                // (C)
Recall Solution
  • (A) PASS. sizeof(char) language ki definition se hamesha 1 hota hai. Value: 1 == 11.
  • (B) FAIL. Literal 0u unsigned hai. Comparison mein -1 unsigned mein convert ho jaata hai, ek bahut bada positive value ban jaata hai (sab bits set), isliye -1 < 0u false → 0 hai. Yeh "usual arithmetic conversions" ka gotcha hai, aur static_assert build refuse kar dega. Computed value: 0.
  • (C) PASS. 1 << 4 bit ko 4 places left shift karta hai = 16. 16 == 161.

Exercise 3.3

Kyun #error check sizeof(int) == 4 ke liye static_assert ko replace nahi kar sakta?

Recall Solution

#error preprocessor mein fire karta hai, jo compiler se pehle chalta hai aur sirf preprocessor tokens aur #if arithmetic macros/integer literals par samajhta hai. Iske paas types ka koi notion nahi hai, isliye yeh sizeof(int) evaluate nahi kar sakta. static_assert ek stage baad, compiler mein chalta hai, jo type sizes jaanta hai. Isliye size/enum checks static_assert mein jaate hain, #error mein nahi.


Level 4 — Synthesis

Exercise 4.1

Ek single static_assert design karo jo guarantee kare ki ek struct mein koi padding nahi hai, yeh check karke ki uska size uske members ke sizes ke sum ke barabar hai:

struct Point { short x; short y; };   // want sizeof == 4

Assertion likho, phir ek platform batao jahan yeh legitimately fail ho sakta hai.

Recall Solution
static_assert(sizeof(struct Point) == sizeof(short) + sizeof(short),
              "struct Point has unexpected padding");

Typical machine par sizeof(short) == 2, isliye hum assert karte hain 4 == 4 → pass. Yeh ek aise platform par legitimately fail ho sakta hai jahan compiler alignment padding insert kare (jaise agar short alignment ne gap force kiya), jisse sizeof(struct Point) 4 se bada ho jaaye. Yahi woh surprise hai jo aap build time par pakadna chahte ho raw byte I/O karne se pehle struct par.

Exercise 4.2

Ek enum aur sizeof check ko ek guard mein combine karo jo fail ho agar bit-mask enum uint8_t mein fit hone se zyada badh jaaye.

#include <stdint.h>
enum Perm { P_READ = 1, P_WRITE = 2, P_EXEC = 4, P_ALL = 7 };

Ek static_assert likho jo ensure kare ki P_ALL ek byte mein fit ho (value ≤ 255) aur uint8_t sach mein ek byte ho.

Recall Solution
static_assert(P_ALL <= 255 && sizeof(uint8_t) == 1,
              "Perm mask must fit in a single uint8_t");

P_ALL ek enum constant hai (7) → constant expression; sizeof(uint8_t) constant hai. Result: 7 <= 255 1 hai, sizeof(uint8_t) == 1 1 hai, 1 && 11 → pass. Agar koi baad mein P_SPECIAL = 256 add kare, aur P_ALL = 511 set kare, toh pehla half 0 ban jaata hai aur build ruk jaata hai.


Level 5 — Mastery

Exercise 5.1

Ek portable header snippet likho jo:

  1. C23 mein compile hone par <assert.h> include kiye bina real C23 keyword static_assert use kare, aur
  2. C11/C17 ke under _Static_assert par fallback kare.

__STDC_VERSION__ macro use karo (201112L = C11, 202311L = C23).

Recall Solution
#if __STDC_VERSION__ >= 202311L
  /* C23: static_assert is a keyword, message optional */
  #define MY_SASSERT(cond, msg) static_assert(cond, msg)
#else
  /* C11/C17: use the raw keyword so no header is required */
  #define MY_SASSERT(cond, msg) _Static_assert(cond, msg)
#endif
 
MY_SASSERT(sizeof(int) >= 2, "int must hold at least 16 bits");

Dono spellings ka matlab ek hi hai; hum sirf <assert.h> par depend hone se bachne ke liye branch karte hain aur is baat ka dhyan rakhte hain ki C23 ne message rule relax kiya. Dekho C11 vs C23 language features.

Exercise 5.2

Ek parser fixed on-disk layout ke records padhta hai. Aapko build time par guarantee karni hai ki: RECORD_SIZE ek power of two hai, kam se kam sizeof(struct Header) hai, aur 4096 bytes ke ek PAGE ko evenly tile karta hai. Diya gaya hai:

#define RECORD_SIZE 64
#define PAGE 4096
struct Header { int magic; int len; };   /* assume sizeof == 8 */

Ek static_assert likho jo teeno conditions enforce kare aur evaluate karo ki yeh pass hota hai ya nahi.

Recall Solution
static_assert(
    (RECORD_SIZE & (RECORD_SIZE - 1)) == 0 &&     /* power of two   */
    RECORD_SIZE >= sizeof(struct Header) &&        /* holds a header */
    (PAGE % RECORD_SIZE) == 0,                      /* tiles the page */
    "RECORD_SIZE constraint violated");

Evaluate karo:

  • Power of two: 64 & 63 = 0 ✔ (64 hai 1000000₂, ek set bit).
  • Header hold karta hai: 64 >= 8 → true ✔.
  • Page tile karta hai: 4096 % 64 = 0 ✔ (4096 / 64 = 64).

Teeno 1 hain, isliye 1 && 1 && 11pass hota hai. RECORD_SIZE ko 100 par change karo aur ek ke alawa har clause fail ho jaata hai: 100 & 99 = 96 ≠ 0 → build ruk jaata hai.

Figure — Compile-time assertions — static_assert

Wrap-up recall

Upar ke solutions ke liye numeric checks verification block mein hain.