মনে করো, তোমার বন্ধুর নাম ‘আরিফ’। আরিফ একটা বাড়িতে থাকে।
কোডে এটি বুঝতে দুটি চিহ্ন খুব গুরুত্বপূর্ণ:
int x = 10; // একটি সাধারণ ভেরিয়েবল
int *ptr = &x; // ptr এখন x এর ঠিকানা (address) ধরে রেখেছে
cout << x; // আউটপুট: 10
cout << ptr; // আউটপুট: x এর মেমোরি অ্যাড্রেস (যেমন: 0x61ff08)
cout << *ptr; // আউটপুট: 10 (ঠিকানা ধরে ভ্যালু বের করা)
যেকোনো প্রসেস যখন মেমোরিতে লোড হয়, তখন সেটি মূলত ৪টি প্রধান ভাগে ভাগ হয়:
এখানে তোমার লেখা আসল কোড বা ইনস্ট্রাকশনগুলো থাকে।
এখানে থাকে Global এবং Static ভেরিয়েবলগুলো। এটি আবার দুই ভাগ:
স্ট্যাক কাজ করে LIFO (Last In First Out) পদ্ধতিতে।
হিপ হলো মেমোরির বিশাল একটা খালি জায়গা। এটি নিচ থেকে ওপরের দিকে বাড়ে।
এটি সিস্টেম লেভেলের কনসেপ্ট:
সিনিয়ররা জানে কখন গারবেজ কালেক্টর ট্রিগার হয়।
কীভাবে কাজ করে? এটি হিপ মেমোরি মনিটর করে। যখন দেখে কোনো অবজেক্টের সাথে প্রোগ্রামের কোনো সরাসরি কানেকশন বা “Reference” নেই, তখন সে বুঝে নেয় এই অবজেক্টটি এখন আবর্জনা। সে তখন সেটাকে মেমোরি থেকে মুছে দেয়।
ঝামেলা কোথায়? গারবেজ কালেকশন যখন চলে, তখন অনেক সময় প্রোগ্রাম সামান্য সময়ের জন্য থমকে যায় (একে বলে Stop-the-world ইভেন্ট)। হাই-পারফরম্যান্স সিস্টেম বানানোর সময় ইঞ্জিনিয়াররা এমন কোড লেখে যাতে GC-কে কম কাজ করতে হয়।
মেমোরি লিক তখন হয় যখন তুমি হিপ মেমোরিতে জায়গা নিয়েছ কিন্তু কাজ শেষ হওয়ার পর সেটাকে আর ছাড়োনি (Free করোনি)।
// ১. Data Segment (Global variable)
int totalVisitors = 1000;
void calculateDiscount(int price) {
// ২. Stack (Local variable)
int discount = 10;
// ৩. Heap (Dynamic memory allocation)
int* finalPrice = new int;
*finalPrice = price - discount;
cout << *finalPrice;
}
int main() {
calculateDiscount(500);
return 0;
}
১. Code Segment (Text):
তুমি যে if-else, cout, বা calculateDiscount ফাংশনটা লিখেছ, সেটার ইনস্ট্রাকশনগুলো এখানে থাকে। এটি Read-only। এখানে হাত দেওয়ার ক্ষমতা কারোর নেই।
২. Data Segment:
এখানে তোমার totalVisitors = 1000ভেরিয়েবলটি থাকবে। এটি প্রোগ্রাম শুরু হওয়া থেকে শেষ হওয়া পর্যন্ত মেমোরি দখল করে রাখবে। কারণ এটি Global।
৩. Stack Memory (লোকাল জগতের রাজা):
যখন main() থেকে calculateDiscount(500) কল হলো, তখন স্ট্যাকে একটা Stack Frame তৈরি হয়।
৪. Heap Memory (অস্থায়ী বড় জায়গা): খেয়াল করো, আমরা লিখেছি new int। এটি মেমোরির একটা বিশাল খালি জায়গায় (Heap) একটা ছোট ঘর তৈরি করে।
finalPrice এই 0xABC অ্যাড্রেসটাকে ধরে রাখে।৫. User Space বনাম Kernel Space (বাস্তব উদাহরণ)
৬. Garbage Collection & Memory Leak (সিনিয়র লেভেল কনসেপ্ট)
উপরের কোডে new int দিয়ে হিপ-এ জায়গা নিয়েছি কিন্তু delete করিনি।
কম্পিউটার মেমোরিতে একটি প্রোগ্রাম কীভাবে জায়গা দখল করে এবং কাজ করে, তার একটি ভিজ্যুয়াল রিপ্রেজেন্টেশন নিচে দেওয়া হলো:
High Address +---------------------------------------+
| Kernel Space (OS) | <--- সিস্টেম কল ও হার্ডওয়্যার হ্যান্ডেল করে
+---------------------------------------+
| Stack (🔽 নিচের দিকে বাড়ে) | <--- লোকাল ভেরিয়েবল, ফাংশন আর্গুমেন্ট
| (LIFO: Last In First Out) |
+---------------------------------------+
| Unused Memory Space |
| (মেমোরি ফাঁকা থাকলে বাড়ে) |
+---------------------------------------+
| Heap (🔼 ওপরের দিকে বাড়ে) | <--- Dynamic Memory (new, malloc)
| (সবচেয়ে বড় এবং ফ্লেক্সিবল জায়গা) |
+---------------------------------------+
| Uninitialized Data (BSS) | <--- ভ্যালু ছাড়া Global/Static (e.g., int x;)
+---------------------------------------+
| Initialized Data Segment | <--- ভ্যালুসহ Global/Static (e.g., int x=10;)
+---------------------------------------+
| Code / Text Segment (Read-only) | <--- তোমার আসল কোড (Binary instructions)
Low Address +---------------------------------------+
```cpp
int global_var = 50; // Data Segment (Initialized)
int uninitialized_var; // BSS Segment
void myFunc() {
int local_var = 10; // Stack Memory
int* h_ptr = new int(20); // h_ptr থাকে Stack-এ, কিন্তু 20 থাকে Heap-এ
}
সহজ কথায়: Stack হচ্ছে মেমোরির একটি ছোট কিন্তু সুপার ফাস্ট ড্রয়ার, আর Heap হচ্ছে একটি বিশাল গুদাম ঘর।
int, boolean) এবং বড় ডাটার Reference (ঠিকানা)।#include <iostream>
using namespace std;
int main() {
int x=5;
int* p= &x;
cout<< p;
cout<< *p;
return 0;
}
Variable Address Value +———+———–+———–+ | x | 0x101 | 5 | <– x এর নিজের মান ৫ +———+———–+———–+ | p | 0x202 | 0x101 | <– p এর ভেতর x এর ঠিকানা (0x101) +———+———–+———–+
#include <iostream>
using namespace std;
// এখানে *x এবং *y হলো পয়েন্টার যারা আসল ঠিকানাকে পয়েন্ট করছে
void swap(int* x, int* y) {
int temp = *x; // x-এর ঠিকানায় যে ভ্যালু (১০) আছে তা temp-এ রাখো
*x = *y; // y-এর ঠিকানায় থাকা ভ্যালু (২০) কে x-এর ঠিকানায় বসাও
*y = temp; // temp-এর ভ্যালু (১০) কে y-এর ঠিকানায় বসাও
}
int main() {
int a = 10, b = 20;
// &a এবং &b দিয়ে আমরা অরিজিনাল অ্যাড্রেস পাঠাচ্ছি
swap(&a, &b);
cout << "a: " << a << ", b: " << b; // আউটপুট: a: 20, b: 10
return 0;
}
সহজ কথায়, এটি হলো “পয়ন্টারের পয়েন্টার”।
বাস্তব উদাহরণ: মনে করো, ‘আরিফ’ একটা বাড়িতে থাকে।
int x = 10;
int* p = &x; // p এখন x এর ঠিকানা জানে
int** pp = &p; // pp এখন p এর নিজের ঠিকানা জানে
cout << x; // ১০
cout << *p; // ১০ (x এর ভ্যালু)
cout << **pp; // ১০ (p এর কাছে গিয়ে তার ঠিকানায় থাকা x এর ভ্যালু)
const মানে হলো “স্থির” বা যা পরিবর্তন করা যায় না। পয়েন্টারের সাথে এটি ৩ ভাবে কাজ করে এবং ইন্টারভিউতে এটি প্রচুর জিজ্ঞেস করা হয়:
পয়ন্টার যাকে পয়েন্ট করছে (Value), তার মান বদলানো যাবে না। কিন্তু পয়েন্টার নিজে অন্য ঠিকানায় যেতে পারবে।
const int* p = &x;
// *p = 20; <-- ভুল (মান বদলানো নিষেধ)
p = &y; // ঠিক (অন্য ঠিকানায় যেতে পারবে)
পয়ন্টার যাকে পয়েন্ট করছে (Value), তার মান বদলানো যাবে না। কিন্তু পয়েন্টার নিজে অন্য ঠিকানায় যেতে পারবে।
const int* p = &x;
// *p = 20; <-- ভুল (মান বদলানো নিষেধ)
p = &y; // ঠিক (অন্য ঠিকানায় যেতে পারবে)
পয়ন্টার একবার যার ঠিকানা নিয়েছে, সে আর অন্য কোথাও যেতে পারবে না। কিন্তু সে যাকে পয়েন্ট করছে তার ভ্যালু বদলাতে পারবে।
int* const p = &x;
*p = 20; // ঠিক (ভ্যালু বদলানো যাবে)
// p = &y; <-- ভুল (ঠিকানা বদলানো নিষেধ)
সবকিছু লক! না ভ্যালু বদলানো যাবে, না ঠিকানা।
const int* const p = &x;
// না ভ্যালু বদলাবে, না ঠিকানা।
Memory Level: [ x: 10 ] <— Address: 0x101 ^ | [ p: 0x101 ] <— Address: 0x202 (Simple Pointer) ^ | [ pp: 0x202 ] <— Address: 0x303 (Double Pointer)
এটি ঘটে যখন তুমি Heap মেমোরিতে কোনো জায়গা দখল করো (যেমন: new বা malloc দিয়ে), কিন্তু কাজ শেষে তা আর মুক্ত (delete বা free) করো না।
তুমি মেমোরি মুক্ত করে দিয়েছ (delete করেছ), কিন্তু পয়েন্টার ভেরিয়েবলটির কাছে এখনও সেই পুরোনো ঠিকানাটা রয়ে গেছে।
সহজ উদাহরণ: একটি বাড়ি ভেঙে ফেলা হয়েছে (মেমোরি ডিলিট), কিন্তু তোমার কাছে এখনও ওই বাড়ির ঠিকানাটা ডায়েরিতে লেখা আছে। এখন তুমি যদি ওই ঠিকানায় গিয়ে ঘরে ঢোকার চেষ্টা করো, তুমি আসলে এমন কোথাও পা দিচ্ছ যা এখন আর অস্তিত্বশীল নয়।
ফলাফল: এর ফলে Segmentation Fault বা Undefined Behavior দেখা দেয়। (প্রোগ্রাম হুটহাট অদ্ভুত কাজ শুরু করে)।
এটি খুবই ভয়ংকর ভুল। একই মেমোরি পয়েন্টার বা ঠিকানাকে যদি তুমি দুইবার free বা delete করার চেষ্টা করো, তবেই এটি হয়।