The Preprocessor & Macros
Compiler চালু হওয়ার আগে যা ঘটে
1. Before the Compiler Runs
Preprocessor একটি text-substitution tool যা actual compilation-এর আগে চালু হয়। # দিয়ে শুরু হওয়া সব directive এই stage-এ process হয়।
#define আসলে শুধু একটি search-and-replace।2. #include and Header Guards
// task.h
#ifndef TASK_H
#define TASK_H
typedef struct { int id; char text[128]; } Task;
void add_task(const char *text);
#endif // TASK_H
Header guard একই ফাইল একাধিকবার include হওয়া থেকে বাঁচায়। আধুনিক বিকল্প: #pragma once (widely supported, non-standard)।
3. Object-like and Function-like Macros
#include <stdio.h>
#define PI 3.14159265
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))
int main(void) {
printf("PI = %f\n", PI);
printf("max(7, 13) = %d\n", MAX(7, 13));
printf("square(1+2) = %d\n", SQUARE(1 + 2)); // 9, not 5
return 0;
}
#define SQUARE(x) x * x লিখলে SQUARE(1+2) হয়ে যায় 1+2*1+2 = 5। তাই সবসময় ((x) * (x)) লিখুন।
4. Conditional Compilation
#include <stdio.h>
#define DEBUG 1
int main(void) {
int x = 42;
#if DEBUG
printf("[DEBUG] x = %d\n", x);
#endif
printf("result = %d\n", x * 2);
#if __STDC_VERSION__ >= 201112L
puts("Running on C11 or later");
#endif
return 0;
}
Command-line-এও define করা যায়: gcc -DDEBUG program.c।
5. Stringification (#) and Token-Pasting (##)
#include <stdio.h>
#define STR(x) #x
#define STR2(x) STR(x)
#define JOIN(a, b) a##b
#define ADD_TEN(x) ((x) + 10)
#define VERSION 2026
int main(void) {
printf("STR(hello) = %s\n", STR(hello));
printf("STR(1+2) = %s\n", STR(1+2));
printf("STR2(VERSION) = %s\n", STR2(VERSION)); // "2026"
int JOIN(count, 1) = 7;
printf("count1 = %d\n", count1);
printf("ADD_TEN(5) = %d\n", ADD_TEN(5));
return 0;
}
#x আর্গুমেন্টকে string literal বানায়। a##b দুটি token জোড়া লাগিয়ে একটি নতুন identifier তৈরি করে। STR2-এর মতো দুই স্তর দরকার যখন ভিতরের মান প্রথমে expand হতে হবে।6. Predefined Macros
#include <stdio.h>
#define LOG(msg) \
printf("[%s:%d %s] %s\n", __FILE__, __LINE__, __func__, msg)
int main(void) {
LOG("starting up");
LOG("doing work");
LOG("shutting down");
return 0;
}
বড় প্রোজেক্টে debug log-এ __FILE__, __LINE__ ও __func__ দিয়ে ঠিক কোন লাইনে কী হচ্ছে সেটি ট্র্যাক করা যায়।
7. Prefer const / enum over #define
// Weak — no type checking, no scope
#define MAX_USERS 100
// Better — typed, scoped
enum { MAX_USERS = 100 };
static const int max_users = 100;
const বা enum ব্যবহার করুন।8. Practice Problems
- Write a
MINmacro with proper parenthesization.সঠিকভাবে parenthesize করাMINmacro লিখুন।✨ Show Answer
min.c#include <stdio.h> #define MIN(a, b) ((a) < (b) ? (a) : (b)) int main(void) { printf("%d\n", MIN(7, 3)); printf("%d\n", MIN(2 + 3, 4)); return 0; } - Write a
SWAP(a,b)macro. What goes wrong across types?SWAP(a,b)macro লিখুন — type mismatch হলে কী হয়?✨ Show Answer
swap_macro.c#include <stdio.h> #define SWAP(a, b) do { typeof(a) _t = (a); (a) = (b); (b) = _t; } while (0) int main(void) { int x = 1, y = 2; SWAP(x, y); printf("%d %d\n", x, y); double a = 1.5, b = 2.5; SWAP(a, b); printf("%.1f %.1f\n", a, b); return 0; }typeofGCC extension। প্লেইন#define SWAP(a,b) { int t = a; a = b; b = t; }শুধুint-এ চলে; অন্য type-এ ব্যবহার করলে compile error বা truncation। - Show how
SQUARE(i++)causes a bug.SQUARE(i++)কেন ভুল ফল দেয় — দেখান।✨ Show Answer
sq_bug.c#include <stdio.h> #define SQUARE(x) ((x) * (x)) int main(void) { int i = 3; int r = SQUARE(i++); // expands to ((i++) * (i++)) — UB printf("r = %d, i = %d\n", r, i); return 0; }Macro argument side-effect সহ হলেই বিপদ। Inline function হলে argument মাত্র একবার evaluate হয় — এজন্য modern code-এ inline function preferred।
- Use
__LINE__and__FILE__to build aLOG(msg)macro.__LINE__ও__FILE__দিয়েLOGmacro লিখুন।✨ Show Answer
Section 6-এর
predefined.c-ই উত্তর। - Conditional compile — build with and without a DEBUG flag, show the difference.DEBUG flag দিয়ে ও ছাড়া compile করে পার্থক্য দেখান।
✨ Show Answer
gcc -DDEBUG=1 prog.c -o prog_dbg # with DEBUG logs gcc prog.c -o prog_rel # without DEBUG logs # inside prog.c: #ifdef DEBUG printf("[DEBUG] %d\n", x); #endifRelease build-এ DEBUG lines সম্পূর্ণ সরে যায় — runtime cost শূন্য।
- Write an assertion macro
ASSERT(x)that prints file/line on failure.ব্যর্থ হলে file/line প্রিন্ট করে — এমনASSERTmacro লিখুন।✨ Show Answer
assert_macro.c#include <stdio.h> #include <stdlib.h> #define ASSERT(x) \ do { if (!(x)) { \ fprintf(stderr, "ASSERT failed: %s at %s:%d\n", #x, __FILE__, __LINE__); \ exit(1); \ } } while (0) int main(void) { int x = 10; ASSERT(x > 0); // passes puts("reached end"); return 0; } - Explain the difference between
#include <x.h>and#include "x.h".#include <x.h>আর#include "x.h"-এর পার্থক্য।✨ Show Answer
<x.h>— system include path-এ খোঁজা হয় (যেমন/usr/include)।"x.h"— আগে current source file-এর পাশে খোঁজা হয়, না পেলে system path-এ। নিজের header-এর জন্য" ", standard/system header-এর জন্য< >। - Add header guards to three of your own headers.নিজের তিনটি header-এ guard যোগ করুন।
✨ Show Answer
// task.h #ifndef TASK_H #define TASK_H /* declarations ... */ #endif // or simply: #pragma onceGuard-এর নাম project-ভিত্তিক unique হলে ভালো:
ABCL_TASK_H। - Use
##to generateregister1,register2, ... names.##দিয়েregister1,register2ইত্যাদি নাম তৈরি করুন।✨ Show Answer
register.c#include <stdio.h> #define REG(n) register##n int main(void) { int REG(1) = 10, REG(2) = 20, REG(3) = 30; printf("%d %d %d\n", register1, register2, register3); return 0; } - Why are macros not type-safe? Show a concrete example.Macro কেন type-safe নয়? উদাহরণ দিন।
✨ Show Answer
Macro শুধু text replace করে — type চেক করে না।
MAX("hello", 42)-ও compile হয়ে যেতে পারে এবং অদ্ভুত result দিতে পারে। এজন্যই সম্ভব হলে inline function বেশি ভালো: type checked, argument একবারই evaluate হয়। - When would you choose a macro over a function?কখন function-এর চেয়ে macro পছন্দ করবেন?
✨ Show Answer
(১) Generic code যেখানে type-independent হতে হবে (
MAX,SWAP,container_of)। (২) Source-location logging (__FILE__,__LINE__inline value-হিসেবে)। (৩) Stringification/token pasting দরকার হলে — function-এ সম্ভব নয়। অন্যসব ক্ষেত্রে inline function নিন। - Run
gcc -Eon your program and observe the expanded output.gcc -Eচালিয়ে preprocessor-এর output দেখুন।✨ Show Answer
gcc -E program.c | less # or save: gcc -E program.c -o program.iOutput-এ দেখবেন —
#include-এর কারণে হাজার হাজার line বসে গেছে, আপনার macro গুলো expand হয়ে গেছে। Preprocessor কী কাজ করে পুরো পরিষ্কার হয়ে যাবে। - Build a platform switch:
#ifdef _WIN32 ... #else ... #endif.Platform switch লিখুন।✨ Show Answer
platform.c#include <stdio.h> int main(void) { #ifdef _WIN32 puts("Running on Windows"); #elif defined(__APPLE__) puts("Running on macOS"); #elif defined(__linux__) puts("Running on Linux"); #else puts("Running on an unknown platform"); #endif return 0; } - Find a real-world macro abuse in any open-source project and explain its risk.কোনো real project-এ একটি macro abuse খুঁজে ঝুঁকি ব্যাখ্যা করুন।
✨ Show Answer
সাধারণ ঝুঁকি: parenthesization ভুল (operator-precedence bug), multiple-evaluation (side-effect), hidden control-flow (
#define CHECK(x) if (!x) return— caller-এreturnচলে আসে)। এসব macro debug করা কঠিন কারণ debugger-এ source line দেখা যায় কিন্তু আসলে অন্য কিছু ঘটছে। সমাধান: inline function, অথবা অন্ততdo { ... } while (0)wrapping।
Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
| Preprocessor | Text-substitution stage that runs before compilation. | Compile-এর আগে চলা text-replacement ধাপ। |
| Directive | A line starting with # that the preprocessor handles. | #-শুরু-হওয়া preprocessor-নির্দেশ। |
#define | Defines a macro or a constant. | Macro বা constant সংজ্ঞায়িত করা। |
#include | Pastes a header file into the source. | Header file source-এ যুক্ত করা। |
| Object-like Macro | Macro without parameters (a constant). | Parameter-হীন macro। |
| Function-like Macro | Macro with parameters — text expansion. | Parameter-যুক্ত macro — text হিসেবে expand হয়। |
| Include Guard | #ifndef ... #define ... #endif to prevent double inclusion. | Header একাধিকবার include হওয়া রোধে guard। |
#pragma once | Compiler-specific include guard. | Compiler-specific include guard। |
| Conditional Compilation | #ifdef, #if, #else, #endif — choose code at build time. | Build-time-এ কোড বাছাই। |
Stringification (#) | Turns a macro arg into a string literal. | Macro argument-কে string-এ রূপান্তর। |
Token Pasting (##) | Concatenates two tokens during expansion. | Expansion-এ দুটি token জুড়ে দেওয়া। |
| Predefined Macros | __FILE__, __LINE__, __DATE__, etc. | আগে থেকেই সংজ্ঞায়িত macro। |
Summary — Module 21
Preprocessor text-level, compiler-এর আগেই চলে। #include, header guard, #define যত্নে ব্যবহার করুন। প্রতিটি macro argument parenthesize করুন। Value-এর জন্য const/enum preferred। Conditional compilation platform ও debug build সামলায়।