এই পর্বে যা শিখবেন
- কোড লেখা থেকে প্রোগ্রাম চালানো পর্যন্ত চারটি ধাপ — কোন ধাপ কী কাজ করে
- যেকোনো error দেখে বলতে পারা — সমস্যাটি কোন ধাপের, কোথায় খুঁজবেন
- বড় সফটওয়্যার কেন টুকরো টুকরো করে compile হয়, আর linker কীভাবে জোড়া দেয়
- Editor, compiler, debugger, sanitizer — কোনটির কী ভূমিকা
- AI-এর লেখা কোড যাচাইয়ের একটি পেশাদার চেকলিস্ট
১ · কম্পিউটার আসলে C বোঝে না
একটি পরিচিত দৃশ্য দিয়ে শুরু করি। রাফি ChatGPT-কে বলল, “একটা C প্রোগ্রাম লিখে দাও।” কয়েক সেকেন্ডেই কোড চলে এলো। কিন্তু নিজের ল্যাপটপে চালাতে গিয়েই স্ক্রিনে ভেসে উঠল:
/usr/bin/ld: main.o: undefined reference to `calculate_total'
collect2: error: ld returned 1 exit status
রাফি তো অবাক — “ld আবার কে? আমি তো এমন কিছু লিখিনি!” এই পর্ব শেষে আপনি এই মেসেজ দেখেই বুঝবেন সমস্যাটি কোথায়, কেন হলো, আর AI-কে ঠিক কী বললে এক চেষ্টাতেই ঠিক হবে।
তার আগে একটি মৌলিক সত্য জেনে নেওয়া দরকার। আপনার লেখা C কোডের একটি অক্ষরও কম্পিউটারের CPU (কম্পিউটারের “মস্তিষ্ক”) সরাসরি বোঝে না। CPU বোঝে শুধু machine code — 0 আর 1-এর লম্বা সারি।
মানুষ থেকে মেশিন পর্যন্ত
From human-readable code to CPU instructions
- আপনি লেখেন
printf("Hi");মানুষ পড়তে পারে - Toolchain
gcc / clangকয়েক ধাপে অনুবাদ - CPU চালায়
01001000 01101001শুধু এটুকুই বোঝে
ধরুন আপনি বাংলায় একটি চিঠি লিখলেন, কিন্তু প্রাপক শুধু জাপানি বোঝেন। তখন একজন অনুবাদক লাগবে। প্রোগ্রামিংয়েও তাই — আপনার C কোড হলো বাংলা চিঠি, CPU হলো জাপানি পাঠক, আর toolchain হলো সেই অনুবাদক। পার্থক্য একটাই: এই অনুবাদক একজন নয়, চারজন — একজনের কাজ শেষ হলে পরেরজনের হাতে তুলে দেন।
Compiled language (যেমন C): পুরো কোড আগেই একবারে machine code-এ অনুবাদ হয়ে চালানো যায় এমন একটি ফাইল তৈরি হয়। পরে যতবার খুশি চালান, আর অনুবাদ লাগে না। Interpreted language (যেমন Python): একজন interpreter চালানোর সময় কোড পড়ে পড়ে চালায়। এজন্যই C সাধারণত অনেক দ্রুত, আর Python-এ “Run” চাপলেই সব হয়ে যায় — ভেতরের ধাপগুলো লুকানো থাকে।
১৯৭২ সালে Bell Labs-এ Dennis Ritchie C ভাষা তৈরি করেন — মূলত Unix অপারেটিং সিস্টেম লেখার জন্য। ১৯৮৭ সালে Richard Stallman-এর GNU প্রজেক্ট প্রকাশ করে ফ্রি compiler GCC, যা আজও Linux জগতের ভিত্তি। আর ২০০৭ সালে Apple শুরু করে Clang, যা দাঁড়িয়ে আছে Chris Lattner-এর তৈরি LLVM কাঠামোর ওপর। আজকের প্রায় সব C প্রোগ্রাম এই দুটির কোনো একটি দিয়ে তৈরি হয়।
২ · চার ধাপের কারখানা
বাংলাদেশের একটি গার্মেন্টস কারখানার কথা ভাবুন। ডিজাইনার একটি শার্টের নকশা আঁকলেন। কিন্তু নকশা তো আর গায়ে দেওয়া যায় না! নকশা থেকে আসল শার্ট হতে হলে কয়েকটি সেকশন পার হতে হয় — স্টোর থেকে কাপড় আনা, কাটিং, আলাদা টুকরো, তারপর সেলাই লাইন।
আপনার C কোডও ঠিক এভাবে চারটি “সেকশন” পার হয়। নিচের ডায়াগ্রামে “পরে” চাপুন, অথবা “চালান” চাপলে নিজে থেকেই এগোবে। প্রতিটি ধাপে ফাইলের ভেতরের জিনিস কীভাবে বদলে যাচ্ছে, সেটি খেয়াল করুন।
- আপনার কোড
hello.cনকশা - Preprocessor
hello.iস্টোর থেকে মাল আনা - Compiler
hello.sকাটিং প্যাটার্ন - Assembler
hello.oকাটা টুকরো - Linker
hello.exeসেলাই করা শার্ট
#include <stdio.h>
int main(void) {
printf("Hello, Dhaka!\n");
return 0;
}
/* stdio.h থেকে আনা কয়েকশো লাইন … */ int printf(const char *format, ...); /* … আরও শত শত লাইন … */ int main(void) { printf("Hello, Dhaka!\n"); return 0; }
main:
push rbp
mov rbp, rsp
lea rdi, [rip+.LC0] ; "Hello, Dhaka!" কোথায় আছে
call puts ; লেখা ছাপার ফাংশন ডাকো
mov eax, 0
pop rbp
ret
55 48 89 e5 48 8d 3d 00 00 00 00 e8 ?? ?? ?? ?? b8 00 00 00 00 5d c3 ↑ ?? = “puts কোথায় আছে জানি না” — ফাঁকা জায়গা
$ ./hello
Hello, Dhaka!
- ধাপ ১ — নকশা। আপনি (বা AI)
hello.cফাইলে C কোড লিখলেন। এটি source code — মানুষ পড়তে পারে, কিন্তু CPU এখনো কিছুই বোঝে না। ঠিক যেন কাগজে আঁকা শার্টের নকশা। - ধাপ ২ — Preprocessor: স্টোর থেকে মাল আনা।
#include <stdio.h>মানে “stdio.h ফাইলটি এনে এখানে বসাও।” Preprocessor ঠিক সেটাই করে — কাটিং শুরুর আগে কাপড়, সুতা, বোতাম টেবিলে সাজিয়ে রাখার মতো। ফাইলের নাম ভুল হলে এই ধাপেই কাজ আটকে যায়। - ধাপ ৩ — Compiler: কাটিং প্যাটার্ন। এটিই আসল অনুবাদক। Compiler পুরো কোড পড়ে, ব্যাকরণ (syntax) ঠিক আছে কি না দেখে, তারপর assembly-তে অনুবাদ করে। একটি
;বাদ পড়লেও এই ধাপেই ধরা পড়বে। - ধাপ ৪ — Assembler: কাপড় কাটা। Assembler assembly-কে আসল 0/1-এ বদলায়, তৈরি হয় object file (
.o). কিন্তু খেয়াল করুন — কিছু ফাঁকা জায়গা (??) রয়ে গেছে!putsফাংশনটি আপনি লেখেননি, তাই সেটি কোথায় আছে এখনো জানা নেই। হাতা আর কলার কাটা হয়েছে, কিন্তু শার্ট হয়নি। - ধাপ ৫ — Linker: সেলাই করে শার্ট তৈরি। Linker আপনার টুকরোর সাথে library-র রেডিমেড টুকরো (যেমন
puts) জুড়ে ফাঁকা জায়গাগুলো পূরণ করে। তৈরি হলো executable. বোতাম না পেলে যেমন শার্ট অসম্পূর্ণ থাকে, কোনো টুকরো না পেলে linker তেমনি error দেয় — রাফির error ঠিক এটাই!
.c → Preprocessor → Compiler → Assembler → Linkerমজার ব্যাপার হলো, আপনি যখন একটিমাত্র কমান্ড দেন — gcc hello.c -o hello — এই চারটি ধাপ এক সেকেন্ডেরও কম সময়ে পরপর হয়ে যায়, মাঝের ফাইলগুলো কাজ শেষে মুছেও যায়। চাইলে যেকোনো ধাপে থামিয়ে ভেতরের ফাইল দেখা যায়:
| কোথায় থামতে চান | কমান্ড | যা পাবেন |
|---|---|---|
| Preprocessor-এর পরে | gcc -E hello.c | #include বসানো পুরো কোড |
| Compiler-এর পরে | gcc -S hello.c | assembly ফাইল (.s) |
| Assembler-এর পরে | gcc -c hello.c | object file (.o) |
| পুরো পথ | gcc hello.c -o hello | চালানো যায় এমন প্রোগ্রাম |
এই কমান্ডগুলো মুখস্থ করার দরকার নেই। মনে রাখার বিষয় একটাই: “build” কোনো জাদু নয় — এটি কয়েকটি আলাদা ধাপের একটি পাইপলাইন। কোনো ধাপে আটকালে কেবল সেই ধাপটিই খুঁজতে হবে।
৩ · Error দেখে বুঝুন — সমস্যা কোন ধাপের
এই পর্বের সবচেয়ে কাজের অংশ এটি। Error মেসেজ দেখে নতুনরা প্রায়ই ঘাবড়ে যান। কিন্তু একটি গোপন কথা বলি — প্রতিটি error মেসেজের ভেতরেই লেখা থাকে সে কোন ধাপ থেকে এসেছে। সেটি চিনতে পারলে অর্ধেক সমস্যার সমাধান হয়ে যায়।
| কী ভুল হয়েছে | কোন ধাপ ধরে | মেসেজে যে শব্দ খুঁজবেন |
|---|---|---|
Header ফাইলের নাম ভুল (stdoi.h) | 📦 Preprocessor | No such file or directory |
; বা } বাদ পড়েছে | 📐 Compiler | error: expected ';' |
| ভ্যারিয়েবলের নামের বানান ভুল | 📐 Compiler | undeclared |
| ফাংশন ডাকা হয়েছে, কিন্তু তার কোড কোথাও নেই | 🧵 Linker | undefined reference to |
main ফাংশনটাই নেই | 🧵 Linker | undefined reference to `main' |
| শূন্য দিয়ে ভাগ, ভুল মেমরিতে হাত দেওয়া | 💥 চলার সময় (runtime) | Segmentation fault, Floating point exception, crash |
এবার রাফির মেসেজটি আবার পড়ুন: /usr/bin/ld আর ld returned 1. “ld” হলো Linux-এ linker-এর আসল নাম। অর্থাৎ AI কোডে calculate_total ফাংশনটি ডেকেছে, কিন্তু ফাংশনটি লিখে দিতে ভুলে গেছে। সমাধান সহজ — AI-কে বলুন: “calculate_total ফাংশনের পূর্ণ কোড দাও।”
সবচেয়ে বিপজ্জনক ভুল হলো সেটি, যা কোনো ধাপেই ধরা পড়ে না। প্রোগ্রাম দিব্যি চলে, কিন্তু ভুল উত্তর দেয় — যেমন বেতনের হিসাবে ভুল যোগফল। Compiler এখানে সাহায্য করতে পারে না, কারণ কোডের ব্যাকরণ ঠিকই আছে, ভুলটা যুক্তিতে। এগুলো ধরতে লাগে debugger আর আপনার নিজের যাচাই — যা আমরা অংশ ৮-এ দেখব।
৪ · বড় সফটওয়্যার কেন টুকরো করে বানানো হয়
এতক্ষণ আমরা একটিমাত্র ফাইল দেখেছি। কিন্তু বাস্তবের সফটওয়্যার বিশাল। Linux kernel-এ আছে তিন কোটিরও বেশি লাইন কোড, হাজার হাজার ফাইলে ভাগ করা। প্রতিবার একটি লাইন বদলালেই যদি পুরো তিন কোটি লাইন নতুন করে অনুবাদ করতে হতো — প্রোগ্রামাররা সারাদিন শুধু অপেক্ষাই করতেন!
সমাধান হলো separate compilation — প্রতিটি .c ফাইল আলাদাভাবে .o-তে অনুবাদ হয়, আর শেষে linker সব জোড়া দেয়। একটি ফাইল বদলালে শুধু সেটিই নতুন করে compile হয়। নিচের ডায়াগ্রামে দেখুন:
- তিনটি আলাদা ফাইল। ধরুন একটি বেতন-হিসাবের সফটওয়্যার — মূল অংশ, বেতনের হিসাব আর রিপোর্ট, তিনটি আলাদা ফাইলে। আলাদা ফাইল মানে আলাদা মানুষ একসাথে কাজ করতে পারেন।
- প্রতিটি আলাদাভাবে compile. প্রতিটি
.cফাইল নিজে নিজে preprocess → compile → assemble হয়ে.oহলো। এগুলো একে অপরের সম্পর্কে কিছু জানে না — প্রত্যেকের ভেতরে ফাঁকা জায়গা আছে। - শুধু salary.c বদলালেন। লাল দাগ দেওয়া ফাইলটিতে একটি সংশোধন করা হলো। এখন শুধু
salary.c → salary.oনতুন করে তৈরি হবে; বাকি দুটি আগের মতোই থাকবে। হাজার ফাইলের প্রজেক্টে এটাই ঘণ্টাকে সেকেন্ডে নামিয়ে আনে। এই হিসাব রাখার কাজ করে Make বা CMake-এর মতো build system. - Linker সব জোড়া দিল। তিনটি
.oআর standard librarylibcমিলিয়ে linker একটি সম্পূর্ণpayroll.exeতৈরি করল। কোনো ফাঁকা জায়গার মিল না পেলেই —undefined reference.
Library জোড়ার দুই উপায় — Static ও Dynamic
📦 Static linking
Library-র দরকারি কোড সরাসরি আপনার প্রোগ্রামের ভেতরে কপি হয়ে যায়। প্রোগ্রাম বড় হয়, কিন্তু অন্য কোনো ফাইল ছাড়াই যেকোনো কম্পিউটারে চলে।
উদাহরণ: সাধারণত Go-র প্রোগ্রাম🔗 Dynamic linking
Library আলাদা ফাইলে থাকে — Windows-এ .dll, Linux-এ .so. প্রোগ্রাম ছোট থাকে, অনেক প্রোগ্রাম একই library ভাগ করে নেয়। কিন্তু ফাইলটি হারালে প্রোগ্রাম খোলেই না।
কখনো কোনো গেম বা সফটওয়্যার ইনস্টল করে এই মেসেজ দেখেছেন — “VCRUNTIME140.dll was not found”? এটি আসলে একটি dynamic linking-এর সমস্যা! প্রোগ্রামটি খোলার সময় একটি library ফাইল খুঁজছে, কিন্তু আপনার কম্পিউটারে সেটি নেই। এখন থেকে আপনি জানেন ভেতরে আসলে কী ঘটছে।
৫ · আপনার টুলবক্স — কোনটির কী কাজ
নতুনরা প্রায়ই গুলিয়ে ফেলেন — “VS Code-ই তো compiler, তাই না?” না। প্রতিটি টুলের কাজ আলাদা। একটি রান্নাঘরের কথা ভাবুন: খাতায় রেসিপি লেখা, চুলায় রান্না আর চেখে দেখা — তিনটি আলাদা কাজ, আলাদা জিনিস লাগে।
Editor / IDE
কোড লেখার খাতা। লেখা রঙিন করে দেখায়, ভুলের নিচে দাগ দেয়। নিজে কোড চালায় না — ভেতরে compiler-কে ডেকে আনে।
VS Code, Code::BlocksCompiler
আসল অনুবাদক — পুরো toolchain চালায়। কোডকে প্রোগ্রাম বানায়, ভুল থাকলে জানায়।
GCC, ClangDebugger
ক্রিকেটের DRS রিপ্লের মতো — প্রোগ্রাম থামিয়ে থামিয়ে দেখা যায় ভেতরে কী হচ্ছে।
GDB, VS Code DebuggerSanitizer
মেট্রো স্টেশনের সিকিউরিটি গেটের মতো — প্রোগ্রাম চলার সময় মেমরির ভুল হাতেনাতে ধরে।
AddressSanitizer৬ · সেটআপ — পাঁচ মিনিটের কাজ
শুরুতেই কিছু ইনস্টল করার প্রয়োজন নেই — এই কোর্সের কোডগুলো পাতাতেই চালানো যায়। তবে নিজের কম্পিউটারে সেটআপ করলে আসল পেশাদার অভিজ্ঞতা পাবেন। আপনার যেটি আছে, সেই ট্যাবটি বেছে নিন:
কিছুই ইনস্টল করতে না চাইলে ব্রাউজারে খুলুন, কোড পেস্ট করুন, Run চাপুন:
- OnlineGDB — চালানো ও debug, দুটোই করা যায়
- Compiler Explorer — আপনার কোড assembly-তে কেমন দেখায়, পাশাপাশি দেখায় (উপরের ধাপ ৩!)
মোবাইল থেকেও চলে। শেখার শুরুতে এটুকুই যথেষ্ট।
Windows-এ সবচেয়ে নির্ভরযোগ্য উপায় MSYS2 — এতে আসল GCC পাবেন।
- msys2.org থেকে ডাউনলোড করে ইনস্টল করুন।
- Start মেনু থেকে “MSYS2 UCRT64” খুলে নিচের লাইনটি লিখে Enter চাপুন।
gcc --versionলিখে দেখুন — একটি version নম্বর এলেই কাজ শেষ।
pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-gdbUbuntu বা Debian হলে terminal-এ একটি লাইনই যথেষ্ট, তারপর gcc --version দিয়ে নিশ্চিত হোন:
sudo apt install build-essential gdbTerminal খুলে নিচের লাইনটি লিখুন। Mac-এ compiler হলো Clang — মজার ব্যাপার, gcc লিখলেও আসলে Clang-ই চলে।
xcode-select --installসেটআপে আটকে গেলে AI খুব ভালো সহায়ক — শর্ত একটাই, প্রশ্নটি নির্দিষ্ট হতে হবে। কোন OS, কী করেছেন, কী error এসেছে — তিনটিই বলে দিন:
৭ · Warning — Compiler-এর বন্ধুসুলভ সতর্কবার্তা
Compiler দুই রকম মেসেজ দেয়। Error মানে “প্রোগ্রাম বানাতেই পারলাম না।” আর warning মানে “বানালাম ঠিকই, কিন্তু এখানে কিছু একটা গোলমেলে লাগছে।”
নতুনরা প্রায়ই warning উপেক্ষা করেন — “প্রোগ্রাম তো চলছে!” অথচ অভিজ্ঞরা জানেন, বেশিরভাগ আসল বাগ প্রথমে warning হয়েই দেখা দেয়। সমস্যা হলো, compiler নিজে থেকে সব warning দেখায় না — তাকে বলে দিতে হয়। নিচের প্রতিদিনের কমান্ডটির প্রতিটি অংশ ধাপে ধাপে দেখুন:
gcc— কারখানার ম্যানেজারকে ডাকা হলো। তিনি নিজেই চারটি ধাপ (preprocess, compile, assemble, link) পরপর চালাবেন।-Wall— “W” মানে Warning, “all” মানে সব। অর্থাৎ “গুরুত্বপূর্ণ সব সতর্কবার্তা দেখাও।” এই একটি flag অসংখ্য বাগ থেকে বাঁচায়।-Wextra— “আরও কিছু বাড়তি সতর্কবার্তাও দেখাও।”-Wallযেগুলো বাদ রাখে, সেগুলোও ধরা পড়বে।-g— প্রোগ্রামের ভেতরে debug-এর তথ্য রেখে দাও, যাতে পরে debugger বলতে পারে কোন লাইনে কী ঘটছে।hello.c— কোন ফাইলটি অনুবাদ করতে হবে।-o hello— “o” মানে output; তৈরি প্রোগ্রামের নাম হবেhello. না দিলে নাম হয়a.outবাa.exe. এই পুরো লাইনটিই আপনার প্রতিদিনের কমান্ড।
বিশ্বাস হচ্ছে না? নিচের কোডে একটি লুকানো বাগ আছে — == (সমান কি না যাচাই) লেখার কথা, কিন্তু ভুলে = (মান বসানো) লেখা হয়েছে। চালিয়ে দেখুন warning কী বলে, আর ফলাফলটিও কেমন অদ্ভুত আসে:
#include <stdio.h>
int main(void) {
int marks = 40;
if (marks = 100) { // ভুল! হওয়ার কথা ==
printf("Full marks!\n");
}
printf("marks = %d\n", marks);
return 0;
}marks = 100 লাইনটি তুলনা করে না, বরং marks-এ 100 বসিয়ে দেয়। তাই 40 পেলেও “Full marks!” ছাপা হয়, আর শেষে marks হয়ে যায় 100. Warning চালু থাকলে compiler আগেই সাবধান করে: suggest parentheses around assignment used as truth value — অর্থাৎ “আপনি কি সত্যিই এখানে মান বসাতে চাইছেন?”
৮ · Debugger — প্রোগ্রামের স্লো-মোশন রিপ্লে
ক্রিকেটে আম্পায়ার নিশ্চিত না হলে রিপ্লে দেখেন — ফ্রেম ধরে ধরে, থামিয়ে থামিয়ে। Debugger আপনার প্রোগ্রামের সাথে ঠিক এই কাজটিই করে। সাধারণভাবে প্রোগ্রাম চোখের পলকে শেষ হয়ে যায়; debugger দিয়ে আপনি একটি লাইনে breakpoint বসিয়ে বলেন “এখানে থামো”, তারপর এক লাইন এক লাইন করে এগিয়ে দেখেন কোন ভ্যারিয়েবলের মান কখন বদলাচ্ছে।
নিচের প্রোগ্রামটি 1 + 2 + 3 যোগ করছে, আর আপনি debugger চালাচ্ছেন। “পরে” চাপতে থাকুন এবং ডান দিকে ভ্যারিয়েবলের মান খেয়াল করুন:
1#include <stdio.h>23int main(void) {4 int total = 0;5 for (int i = 1; i <= 3; i++) {6 total = total + i;7 }8 printf("total = %d\n", total);9 return 0;10}
| total | |
| i |
- Breakpoint-এ থেমেছে। সবুজ দাগের লাইন ৪ এখনো চলেনি — পরের ধাপে চলবে।
total-এ এখন আবর্জনা মান (?), কারণ এখনো কিছু রাখা হয়নি। - লাইন ৪ চলল:
totalহলো 0. এবার লুপ শুরু হবে। - লুপ শুরু:
i= 1. শর্ত যাচাই — 1 ≤ 3? হ্যাঁ, তাই ভেতরে ঢুকছে। - লাইন ৬ চলল: total = 0 + 1 = 1. খেয়াল করুন, মানটি সবুজ হয়ে উঠেছে — অর্থাৎ এই ধাপে বদলেছে।
iবেড়ে হলো 2. 2 ≤ 3? হ্যাঁ — আবার ভেতরে।- total = 1 + 2 = 3.
iহলো 3. 3 ≤ 3? এখনো সত্য — শেষবারের মতো ভেতরে।- total = 3 + 3 = 6. এরপর
iহবে 4, আর 4 ≤ 3 মিথ্যা — লুপ শেষ। - লুপ থেকে বেরিয়ে এলো।
iএখন আর নেই — সে শুধু লুপের ভেতরেই বাঁচে। এবার লাইন ৮-এ ফল ছাপা হবে। - স্ক্রিনে এলো total = 6. এবার ভাবুন — ফল 6-এর বদলে 3 এলে কী করতেন? এভাবে ধাপে ধাপে দেখলেই ধরা পড়ত যে i = 3 হলে লুপে ঢুকছে না, অর্থাৎ শর্তে
<=-এর বদলে<লেখা হয়েছে। এটাই debugging.
আসল debugger-এ (GDB বা VS Code) এই কাজগুলো কয়েকটি সহজ কমান্ডে হয়। মুখস্থ নয়, শুধু চিনে রাখুন:
| যা করতে চান | GDB-তে | VS Code-এ |
|---|---|---|
| এই লাইনে থামো | break 4 | লাইন নম্বরের বাঁ পাশে ক্লিক (লাল বিন্দু) |
| প্রোগ্রাম চালু করো | run | F5 |
| পরের লাইনে যাও | next | F10 |
| ভ্যারিয়েবলের মান দেখাও | print total | মাউস রাখলেই দেখায় |
| পরের breakpoint পর্যন্ত চলো | continue | F5 |
৯ · AI-এর যুগে toolchain জানার মূল্য
অনেকেই ভাবেন, “AI তো কোড লিখেই দেয়, এত কিছু জেনে কী হবে?” প্রশ্নটি যৌক্তিক। উত্তরটি হলো — AI খুব দ্রুত কাজ করা একজন সহকারী, কিন্তু সে আপনার কম্পিউটারটি দেখতে পায় না। কোড compile করবে আপনার toolchain, error আসবে আপনার স্ক্রিনে, আর ভুল ফল গেলে দায় নেবেন আপনি।
যিনি সিস্টেম বোঝেন, AI তাঁর হাতেই সবচেয়ে কার্যকর।
AI সাধারণত যেসব toolchain-ভুল করে
| AI-এর ভুল | আপনি যা দেখবেন | কোন ধাপ |
|---|---|---|
| ফাংশন ডেকেছে, কিন্তু লিখে দেয়নি | undefined reference to | Linker |
| Linux-এর header দিয়েছে, আপনি Windows-এ | unistd.h: No such file | Preprocessor |
sqrt() ব্যবহার করেছে, কিন্তু math library জুড়তে বলেনি | undefined reference to `sqrt' (সমাধান: কমান্ডের শেষে -lm) | Linker |
| পুরোনো স্টাইলের কোড দিয়েছে | অনেকগুলো warning | Compiler |
Error পেলে AI-কে নিচের কাঠামোয় প্রশ্ন করুন। শেষ লাইনটি খেয়াল করুন — “শুধু যে লাইন বদলাতে হবে।” এতে আপনি বুঝবেন ঠিক কী বদলাল, আর AI অন্য জায়গায় নতুন ভুল ঢোকানোর সুযোগ পাবে না।
AI-এর লেখা কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে কোড-লেখক থেকে সিস্টেম-চিন্তক বানাবে।
প্র ০১C “compile” হয়, Python “interpret” হয় — আসল পার্থক্যটা কোথায়? তাহলে কি C সবসময় ভালো?
পার্থক্যটা অনুবাদ কখন হচ্ছে — চালানোর আগে, না চালানোর সময়।
- Compiled (C, C++, Rust, Go): পুরো কোড আগেই machine code-এ অনুবাদ হয়। চালানোর সময় অনুবাদের কোনো খরচ নেই, তাই খুব দ্রুত। অনেক ভুল চালানোর আগেই ধরা পড়ে। কিন্তু প্রতিটি OS ও CPU-র জন্য আলাদা করে compile করতে হয় — Windows-এর
.exeMac-এ চলে না। - Interpreted (Python, JavaScript): interpreter চলার সময় কোড পড়ে চালায়। লেখা ও পরীক্ষা খুব দ্রুত, এক কোড সব OS-এ চলে। কিন্তু চালানো তুলনামূলক ধীর, আর অনেক ভুল ধরা পড়ে কেবল সেই লাইনটি চলার সময়।
- মাঝামাঝি পথ: বাস্তবে সীমারেখা ঝাপসা। Python আসলে কোডকে প্রথমে bytecode-এ অনুবাদ করে; Java চলে JVM-এ; আর JavaScript-এর আধুনিক ইঞ্জিন চলার সময়ই machine code বানায় (JIT compilation).
তাহলে কোনটা ভালো? প্রশ্নটাই ভুল — প্রশ্ন হলো “কোন কাজের জন্য?” অপারেটিং সিস্টেম, ফোনের firmware, গাড়ির ব্রেক কন্ট্রোলার, ডেটাবেস ইঞ্জিন — যেখানে গতি ও হার্ডওয়্যারের ওপর পূর্ণ নিয়ন্ত্রণ দরকার, সেখানে C. ডেটা বিশ্লেষণ বা দ্রুত প্রোটোটাইপে Python. মজার ব্যাপার হলো, Python নিজেই (CPython) C-তে লেখা, আর NumPy-র ভারী গণনাও ভেতরে C-তে চলে।
মূল উপলব্ধি: ভাষা হলো টুল। একজন ভালো ইঞ্জিনিয়ার trade-off বোঝেন — গতি বনাম সুবিধা, নিয়ন্ত্রণ বনাম নিরাপত্তা — এবং কাজ অনুযায়ী বেছে নেন।
প্র ০২Compiler কি আপনার কোড “বদলে” দেয়? -O0 আর -O2-এর মধ্যে আসল পার্থক্য কী?
হ্যাঁ, দেয় — এবং সেটা খুব কাজের। Optimization চালু করলে (-O2) compiler আপনার কোডের অর্থ ঠিক রেখে রূপ বদলে দ্রুততর করে। কয়েকটি উদাহরণ:
- Constant folding:
60 * 60 * 24লিখলে compiler নিজেই হিসাব করে সরাসরি86400বসিয়ে দেয় — চালানোর সময় গুণই হয় না। - Dead code removal: যে কোড কখনো চলবে না বা যার ফল কোথাও ব্যবহার হয় না, সেটি পুরোপুরি বাদ।
- Inlining: ছোট ফাংশন ডাকার বদলে তার কোড সরাসরি সেই জায়গায় বসিয়ে দেওয়া।
তাহলে সবসময় -O2 নয় কেন? কারণ optimized কোড debug করা কঠিন — debugger-এ দেখবেন কোনো ভ্যারিয়েবল “optimized out”, বা লাইনগুলো ক্রম মেনে চলছে না। তাই পেশাদার নিয়ম: উন্নয়নের সময় -O0 -g, প্রকাশের সময় -O2.
একটি গভীর সতর্কতা: C-তে কিছু কাজের ফল নিয়মে নির্দিষ্ট করা নেই — একে বলে undefined behavior (যেমন array-র সীমার বাইরে পড়া)। Optimizer ধরে নেয় আপনার কোডে এমন কিছু নেই। তাই এমন বাগওয়ালা কোড -O0-এ “ঠিকঠাক” চললেও -O2-এ অদ্ভুত আচরণ করতে পারে। বাস্তবের বহু নিরাপত্তা-ত্রুটির উৎস এটাই।
মূল উপলব্ধি: আপনি compiler-কে বলেন কী চান; কীভাবে করবে, সেটা অনেকটাই compiler-এর সিদ্ধান্ত। তাই নিয়ম মেনে লেখা কোডই নিরাপদ কোড।
প্র ০৩Static নাকি dynamic linking — একটি কোম্পানি কোনটি বেছে নেবে, আর কেন?
এটি একটি ক্লাসিক system design trade-off — কোনো একটি সব ক্ষেত্রে সেরা নয়।
- Dynamic-এর বড় সুবিধা — নিরাপত্তা আপডেট: ২০১৪ সালে OpenSSL library-তে “Heartbleed” নামে গুরুতর নিরাপত্তা-ত্রুটি ধরা পড়ে। যেসব প্রোগ্রাম dynamic linking ব্যবহার করত, সেখানে শুধু একটি library ফাইল আপডেট করলেই সব প্রোগ্রাম নিরাপদ হয়ে যায়। Static হলে প্রতিটি প্রোগ্রাম নতুন করে build করে আবার বিতরণ করতে হতো।
- Dynamic-এর সমস্যা — “DLL hell”: দুটি প্রোগ্রাম একই library-র দুটি ভিন্ন version চাইলে সংঘাত হয়। “অমুক .dll পাওয়া যায়নি” — এই পরিচিত সমস্যাও এখান থেকেই।
- Static-এর সুবিধা — নির্ভরতা নেই: একটি ফাইল কপি করলেই যেকোনো জায়গায় চলে। এজন্যই Go ডিফল্টভাবে static linking করে, আর Rust-ও নিজের library-গুলো প্রোগ্রামের ভেতরেই জুড়ে দেয়; cloud-এ deploy করা সহজ হয়।
বাস্তবে কী হয়? অপারেটিং সিস্টেম ও ডেস্কটপ সফটওয়্যার সাধারণত dynamic (জায়গা বাঁচে, আপডেট সহজ)। সার্ভারের ছোট টুল ও container-এ চলা সার্ভিস প্রায়ই static. আর Docker container একরকম মাঝামাঝি সমাধান — প্রোগ্রাম আর তার সব library একসাথে প্যাক করে দেওয়া।
মূল উপলব্ধি: প্রকৌশলে প্রায় প্রতিটি সিদ্ধান্তই একটি দাম দিয়ে আরেকটি সুবিধা কেনা। “কোনটা সেরা?” না জিজ্ঞেস করে জিজ্ঞেস করুন “কোন পরিস্থিতিতে কোনটা?”
প্র ০৪Compiler নিজেও তো একটি প্রোগ্রাম — তাহলে compiler-কে কি অন্ধভাবে বিশ্বাস করা যায়?
এটি কম্পিউটার বিজ্ঞানের সবচেয়ে বিখ্যাত প্রশ্নগুলোর একটি। ১৯৮৪ সালে Unix-এর সহ-স্রষ্টা Ken Thompson তাঁর Turing Award ভাষণ “Reflections on Trusting Trust”-এ দেখান: একটি compiler-এর ভেতরে এমন গোপন কোড রাখা সম্ভব, যা login প্রোগ্রাম compile করার সময় চুপিচুপি একটি গোপন দরজা (backdoor) বসিয়ে দেবে — আর নিজেকে নতুন করে compile করার সময় সেই কৌশলটিও কপি করে রাখবে। ফলে সোর্স কোড পড়ে কিছুই ধরা যাবে না!
এটি শুধু তাত্ত্বিক ভয় নয়। ২০২৪ সালে Linux-এর একটি বহুল ব্যবহৃত compression library xz-এর build প্রক্রিয়ার ভেতরে একটি সুপরিকল্পিত backdoor লুকানো অবস্থায় ধরা পড়ে — একজন প্রকৌশলী শুধু খেয়াল করেছিলেন যে SSH লগইনে আধা সেকেন্ড বেশি সময় লাগছে। সময়মতো ধরা না পড়লে বিশ্বের অগণিত সার্ভার ঝুঁকিতে পড়ত।
সমাধানের দিকে প্রচেষ্টা:
- Reproducible builds: একই সোর্স থেকে যে কেউ build করলে হুবহু একই ফাইল পাবে — মিল না হলে বোঝা যাবে কোথাও গড়বড়।
- Bootstrapping: খুব ছোট, হাতে যাচাইযোগ্য একটি compiler থেকে ধাপে ধাপে বড় compiler তৈরি করা।
- Supply-chain নিরাপত্তা: কোন library, কোন version, কে বানিয়েছে — সবকিছুর হিসাব রাখা।
মূল উপলব্ধি: আপনার প্রোগ্রাম শুধু আপনার কোড নয় — compiler, library, build script, এমনকি AI-এর দেওয়া কোডও এর অংশ। নিরাপত্তা মানে এই পুরো শিকলটিকে বোঝা। Toolchain জানা তাই শুধু error ঠিক করার জন্য নয়, নিরাপত্তার জন্যও।
প্র ০৫GCC তো আছেই — তাহলে Clang/LLVM বানানোর দরকার পড়ল কেন? এর পেছনের নকশা থেকে কী শেখার আছে?
LLVM-এর পেছনের মূল চিন্তাটি অসাধারণ একটি architecture-এর শিক্ষা। একটি আধুনিক compiler তিন ভাগে ভাগ করা:
- Front-end: একটি নির্দিষ্ট ভাষা (C, Rust, Swift…) পড়ে।
- Middle (optimizer): সব ভাষা একটি সাধারণ “মাঝামাঝি ভাষা”-য় — IR — রূপ নেয়, আর optimization হয় এখানেই।
- Back-end: IR থেকে নির্দিষ্ট CPU-র (Intel, ARM…) machine code বানায়।
কেন এটা জাদুকরী? ধরুন ৫টি ভাষা আর ৫ রকম CPU. প্রতিটি জোড়ার জন্য আলাদা compiler বানালে লাগত ৫ × ৫ = ২৫টি। মাঝখানে একটি সাধারণ IR থাকলে লাগে মাত্র ৫ + ৫ = ১০টি অংশ। নতুন ভাষা বানাতে চাইলে শুধু একটি front-end লিখলেই সব CPU-তে চলবে! Rust ও Swift ঠিক এভাবেই LLVM ব্যবহার করে।
আরও কিছু কারণ: Clang-এর error message অনেক পরিষ্কার ছিল, যা GCC-কেও নিজের মেসেজ উন্নত করতে বাধ্য করে। লাইসেন্সও ভিন্ন — GCC-র GPL বনাম LLVM-এর বেশি উদার লাইসেন্স, যা Apple-এর মতো কোম্পানির কাছে গুরুত্বপূর্ণ ছিল। প্রতিযোগিতায় শেষ পর্যন্ত দুটোই আরও ভালো হয়েছে।
মূল উপলব্ধি: “মাঝখানে একটি সাধারণ ভাষা বা interface বসিয়ে M×N সমস্যাকে M+N বানানো” — এই কৌশল সফটওয়্যার জগতের সবখানে। USB পোর্ট, ইন্টারনেট প্রোটোকল, এমনকি অনেক ব্যাংক ও মোবাইল ওয়ালেটকে এক জায়গায় জোড়া পেমেন্ট গেটওয়ে — সব জায়গায় একই চিন্তা। একবার চোখে পড়লে আর ভুলবেন না।
অনুশীলন
এগুলোর বেশিরভাগে কোড লিখতে হবে না — হাতে-কলমে দেখা আর ভাবাই আসল কাজ।
-
Assembly চোখে দেখুন: godbolt.org-এ উপরের
marks.cকোডটি পেস্ট করুন। ডান পাশে assembly দেখুন। এবার compiler option ঘরে-O2লিখুন — কী বদলাল?-O0-এ বেশ কিছু লাইনের assembly দেখবেন — প্রায় প্রতিটি C লাইনের হুবহু অনুবাদ।-O2-এ সেটি নাটকীয়ভাবে ছোট হয়ে যায়। কারণ compiler বুঝে ফেলেmarks = 100সবসময় সত্য, তাইif-এর তুলনাটাই বাদ দেয় এবং সরাসরি দুটি লেখা ছাপার নির্দেশ রাখে। এটাই optimization — অর্থ এক, রূপ ভিন্ন। -
Error শিকারি: নিচের তিনটি error কোন ধাপের, বলুন — (ক)
conio.h: No such file or directory(খ)error: 'totl' undeclared(গ)undefined reference to `print_report'(ক) Preprocessor — header ফাইল পাওয়া যায়নি।
conio.hএকটি পুরোনো, শুধু-Windows header; অনেক পুরোনো টিউটোরিয়াল ও AI এটি দেয়, কিন্তু Linux/Mac-এর GCC-তে নেই।(খ) Compiler —
totlনামে কিছু ঘোষণা করা হয়নি; সম্ভবতtotal-এর বানান ভুল।(গ) Linker —
print_reportডাকা হয়েছে, কিন্তু কোনো ফাইলে তার কোড নেই, অথবা যে ফাইলে আছে সেটি build কমান্ডে যোগ করা হয়নি। -
Dynamic linking খুঁজে দেখুন: আপনার কম্পিউটারে
C:\Program Files-এর ভেতরে যেকোনো একটি সফটওয়্যারের ফোল্ডার খুলুন।.exe-এর পাশে কতগুলো.dllফাইল আছে গুনে দেখুন। এগুলো কী?বেশিরভাগ সফটওয়্যারের ফোল্ডারে কয়েক ডজন
.dllপাবেন। এগুলো সেই প্রোগ্রামের dynamic library — প্রোগ্রাম খোলার মুহূর্তে Windows এগুলো খুঁজে এনে জোড়া দেয় (অর্থাৎ linking-এর শেষ অংশটি প্রোগ্রাম খোলার সময় ঘটে)। কোনো একটি মুছে ফেললে খোলার সময় “.dll not found” দেখাবে। (পরীক্ষাটি শুধু দেখার জন্য — কিছু মুছবেন না!) -
AI অডিট: যেকোনো AI-কে বলুন “একটি C প্রোগ্রাম লেখো যা ১০ জন ছাত্রের নম্বর নিয়ে গড় বের করবে।” তারপর উপরের চেকলিস্ট অনুযায়ী যাচাই করুন। কোন কোন জায়গায় সমস্যা পেলেন?
সাধারণত যা দেখা যায়:
-Wall -Wextraদিলে প্রায়ইscanf-এর return value উপেক্ষা করার মতো warning আসে।- অক্ষর দিয়ে নম্বর দিলে (যেমন “abc”) প্রোগ্রাম অদ্ভুত আচরণ করে — AI ইনপুট যাচাই রাখেনি।
- গড় পূর্ণসংখ্যায় হিসাব হয় (
intভাগ), ফলে দশমিক অংশ হারিয়ে যায় — হাতে হিসাব মেলালেই ধরা পড়ে।
এই ভুলগুলো ধরতে পারাটাই আসল দক্ষতা — কোড লেখা নয়, কোড যাচাই করা।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমCompilation-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- ✍️
.cফাইলে কোড লেখা - 📦 Preprocessor —
#includeফাইল এনে বসানো - 📐 Compiler — C থেকে assembly-তে অনুবাদ
- ✂️ Assembler — assembly থেকে 0/1 (
.oফাইল) - 🧵 Linker — library জুড়ে চূড়ান্ত প্রোগ্রাম
CPU সরাসরি কোন ভাষা বোঝে?
- C ভাষা
- Assembly
- Machine code (0 আর 1)
- ইংরেজি
মেসেজ এলো: fatal error: stdoi.h: No such file or directory. কোন ধাপ এটি ধরেছে?
- Preprocessor
- Compiler
- Linker
- প্রোগ্রাম চলার সময়
undefined reference to `calculate_total' — এর অর্থ কী?
- ফাংশনের নামের বানান ভুল, compiler ধরেছে
- একটি header ফাইল পাওয়া যায়নি
- ফাংশন ডাকা হয়েছে, কিন্তু তার কোড কোথাও নেই — linker খুঁজে পায়নি
- প্রোগ্রাম চলতে চলতে crash করেছে
১০০টি ফাইলের একটি প্রজেক্টে মাত্র একটি ফাইল বদলালেন। ভালো build system কী করবে?
- ১০০টি ফাইলই নতুন করে compile করবে
- শুধু বদলানো ফাইলটি compile করবে, তারপর আবার link করবে
- শুধু link করবে, compile লাগবে না
- কিছুই করবে না, পুরোনো প্রোগ্রামই থাকবে
প্রোগ্রাম চলছে, কিন্তু compiler ৩টি warning দিয়েছে। কী করা উচিত?
- প্রোগ্রাম চলছে, তাই উপেক্ষা করা
- প্রতিটি warning পড়ে কারণ বুঝে ঠিক করা
- -Wall flag মুছে দেওয়া, যাতে warning না আসে
- Compiler আবার ইনস্টল করা
AI একটি C প্রোগ্রাম লিখে দিল। এটি ঠিক আছে কি না যাচাইয়ের সবচেয়ে ভালো উপায় কোনটি?
- AI-কেই জিজ্ঞেস করা, “কোডটা ঠিক আছে তো?”
- কোড লম্বা ও জটিল হলে ধরে নেওয়া যায় ঠিক আছে
- একবার চালিয়ে দেখা — চললেই ঠিক
- -Wall দিয়ে warning শূন্য করা, তারপর নানা ইনপুটে চালিয়ে হাতে হিসাব মেলানো
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- CPU বোঝে শুধু 0 আর 1; আপনার C কোড অনুবাদ করে toolchain.
- চার ধাপ: Preprocessor → Compiler → Assembler → Linker.
- Error দেখে আগে বুঝুন কোন ধাপের —
No such file,expected ';',undefined reference. - বড় প্রজেক্টে প্রতিটি ফাইল আলাদা compile হয়, linker শেষে জোড়া দেয়; library জোড়া যায় static বা dynamic ভাবে।
- সবসময়
gcc -Wall -Wextra -g; ভুল ফল এলে debugger; AI-এর কোড সবসময় নিজে যাচাই।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ০৪ · প্রথম প্রোগ্রাম — main()-এর ভেতরে কীপরবর্তী পর্ব
hello.c-এর প্রতিটি লাইনের অর্থ ও উদ্দেশ্য। - C প্রোগ্রামিং · কোর্স হোম৫৬টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- Programming Languages & Compiler Designগভীরে যানCompiler-এর ভেতরে parsing, IR আর code generation কীভাবে কাজ করে।
- Computer Architecture & Digital Logicপাশের ট্র্যাকCPU আসলে machine code কীভাবে চালায় — লজিক গেট থেকে pipeline.
- Operating Systemsপাশের ট্র্যাকপ্রোগ্রাম খুললে OS কীভাবে মেমরিতে আনে ও library জোড়ে।