Memory Management

What is Pointer?

  1. পয়েন্টার হলো এমন একটা ভেরিয়েবল যা অন্য একটা ভেরিয়েবলের মেমোরি অ্যাড্রেস (Memory Address) জমা রাখে।

মনে করো, তোমার বন্ধুর নাম ‘আরিফ’। আরিফ একটা বাড়িতে থাকে।

  1. অ্যাড্রেস এবং ডি-রেফারেন্সিং (Address vs Dereferencing)

কোডে এটি বুঝতে দুটি চিহ্ন খুব গুরুত্বপূর্ণ:

int x = 10;      // একটি সাধারণ ভেরিয়েবল
int *ptr = &x;   // ptr এখন x এর ঠিকানা (address) ধরে রেখেছে

cout << x;       // আউটপুট: 10
cout << ptr;     // আউটপুট: x এর মেমোরি অ্যাড্রেস (যেমন: 0x61ff08)
cout << *ptr;    // আউটপুট: 10 (ঠিকানা ধরে ভ্যালু বের করা)

মেমোরি লেআউট (The 4 Segments)

যেকোনো প্রসেস যখন মেমোরিতে লোড হয়, তখন সেটি মূলত ৪টি প্রধান ভাগে ভাগ হয়:

A. Code Segment (Text Segment):

এখানে তোমার লেখা আসল কোড বা ইনস্ট্রাকশনগুলো থাকে।

B. Data Segment:

এখানে থাকে Global এবং Static ভেরিয়েবলগুলো। এটি আবার দুই ভাগ:

C. Stack (Fast & Auto):

স্ট্যাক কাজ করে LIFO (Last In First Out) পদ্ধতিতে।

D. Heap (The Dynamic Space):

হিপ হলো মেমোরির বিশাল একটা খালি জায়গা। এটি নিচ থেকে ওপরের দিকে বাড়ে।

২. User Space vs Kernel Space

এটি সিস্টেম লেভেলের কনসেপ্ট:

৩. Garbage Collection (GC) - পর্দার আড়ালে সাফাই অভিযান

সিনিয়ররা জানে কখন গারবেজ কালেক্টর ট্রিগার হয়।

৪. Memory Leak: বড় ইঞ্জিনিয়ারদের মাথাব্যথা

মেমোরি লিক তখন হয় যখন তুমি হিপ মেমোরিতে জায়গা নিয়েছ কিন্তু কাজ শেষ হওয়ার পর সেটাকে আর ছাড়োনি (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) একটা ছোট ঘর তৈরি করে।

৫. User Space বনাম Kernel Space (বাস্তব উদাহরণ)

৬. Garbage Collection & Memory Leak (সিনিয়র লেভেল কনসেপ্ট)

উপরের কোডে new int দিয়ে হিপ-এ জায়গা নিয়েছি কিন্তু delete করিনি।

🧠 Memory Layout of a Process

কম্পিউটার মেমোরিতে একটি প্রোগ্রাম কীভাবে জায়গা দখল করে এবং কাজ করে, তার একটি ভিজ্যুয়াল রিপ্রেজেন্টেশন নিচে দেওয়া হলো:

   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-এ
}

📍 মূল কনসেপ্ট (The Core Logic)

সহজ কথায়: Stack হচ্ছে মেমোরির একটি ছোট কিন্তু সুপার ফাস্ট ড্রয়ার, আর Heap হচ্ছে একটি বিশাল গুদাম ঘর।

১. Stack Memory (রেফারেন্স থাকে)

২. Heap Memory (আসল ভ্যালু থাকে)


#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) +———+———–+———–+

Swap Function using Pointers

#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;
}

মেমোরির ভেতরে কী ঘটলো? (Step-by-Step)

Double Pointer - int** p

সহজ কথায়, এটি হলো “পয়ন্টারের পয়েন্টার”।

বাস্তব উদাহরণ: মনে করো, ‘আরিফ’ একটা বাড়িতে থাকে।

int x = 10;
int* p = &x;    // p এখন x এর ঠিকানা জানে
int** pp = &p;  // pp এখন p এর নিজের ঠিকানা জানে

cout << x;    // ১০
cout << *p;   // ১০ (x এর ভ্যালু)
cout << **pp; // ১০ (p এর কাছে গিয়ে তার ঠিকানায় থাকা x এর ভ্যালু)

কেন ডাবল পয়েন্টার লাগে?

Const with Pointers

const মানে হলো “স্থির” বা যা পরিবর্তন করা যায় না। পয়েন্টারের সাথে এটি ৩ ভাবে কাজ করে এবং ইন্টারভিউতে এটি প্রচুর জিজ্ঞেস করা হয়:

Pointer to Constant (const int* p)

পয়ন্টার যাকে পয়েন্ট করছে (Value), তার মান বদলানো যাবে না। কিন্তু পয়েন্টার নিজে অন্য ঠিকানায় যেতে পারবে।

const int* p = &x;
// *p = 20;  <-- ভুল (মান বদলানো নিষেধ)
p = &y;      // ঠিক (অন্য ঠিকানায় যেতে পারবে)

Constant Pointer (int* const p)

পয়ন্টার যাকে পয়েন্ট করছে (Value), তার মান বদলানো যাবে না। কিন্তু পয়েন্টার নিজে অন্য ঠিকানায় যেতে পারবে।

const int* p = &x;
// *p = 20;  <-- ভুল (মান বদলানো নিষেধ)
p = &y;      // ঠিক (অন্য ঠিকানায় যেতে পারবে)

Constant Pointer (int* const p)

পয়ন্টার একবার যার ঠিকানা নিয়েছে, সে আর অন্য কোথাও যেতে পারবে না। কিন্তু সে যাকে পয়েন্ট করছে তার ভ্যালু বদলাতে পারবে।

int* const p = &x;
*p = 20;     // ঠিক (ভ্যালু বদলানো যাবে)
// p = &y;   <-- ভুল (ঠিকানা বদলানো নিষেধ)

Constant Pointer to Constant (const int* const p)

সবকিছু লক! না ভ্যালু বদলানো যাবে, না ঠিকানা।

const int* const p = &x;
// না ভ্যালু বদলাবে, না ঠিকানা।

মেমোরি ম্যাপ

Memory Level: [ x: 10 ] <— Address: 0x101 ^ | [ p: 0x101 ] <— Address: 0x202 (Simple Pointer) ^ | [ pp: 0x202 ] <— Address: 0x303 (Double Pointer)

Memory Leak

এটি ঘটে যখন তুমি Heap মেমোরিতে কোনো জায়গা দখল করো (যেমন: new বা malloc দিয়ে), কিন্তু কাজ শেষে তা আর মুক্ত (delete বা free) করো না।

Dangling Pointer

তুমি মেমোরি মুক্ত করে দিয়েছ (delete করেছ), কিন্তু পয়েন্টার ভেরিয়েবলটির কাছে এখনও সেই পুরোনো ঠিকানাটা রয়ে গেছে।

Double Delete

এটি খুবই ভয়ংকর ভুল। একই মেমোরি পয়েন্টার বা ঠিকানাকে যদি তুমি দুইবার free বা delete করার চেষ্টা করো, তবেই এটি হয়।