Memory Management in C and C++

Memory Distribution in C/C++

Memory Layout Diagram

int globalVar = 1;
static int staticGlobalVar = 1;

void Test()
{
    static int staticVar = 1;
    int localVar = 1;
    int num1[10] = {1, 2, 3, 4};
    char char2[] = "abcd";
    const char* pChar3 = "abcd";
    int* ptr1 = (int*)malloc(sizeof(int) * 4);
    int* ptr2 = (int*)calloc(4, sizeof(int));
    int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
    free(ptr1);
    free(ptr3);
}

Questions:

  1. Multiple Choice (Options: A. Stack, B. Heap, C. Data Segment (Static Area), D. Code Segment (Constant Area)):

    • Where is globalVar? ___
    • Where is staticGlobalVar? ___
    • Where is staticVar? ___
    • Where is localVar? ___
    • Where is num1? ___
    • Where is char2? ___
    • Where is *char2? ___
    • Where is pChar3? ___
    • Where is *pChar3? ___
    • Where is ptr1? ___
    • Where is *ptr1? ___
  2. Fill in the blanks:

    • sizeof(num1) = ____
    • sizeof(char2) = ____
    • strlen(char2) = ____
    • sizeof(pChar3) = ____
    • strlen(pChar3) = ____
    • sizeof(ptr1) = ____
  3. What is the difference between sizeof and strlen?

Explanation:

  1. Stack: Stores non-static local variables, function parameters, return values, etc. The stack grows downwards.
  2. Memory Mapping Segment: An efficient I/O mapping mechanism used to load shared dynamic libraries. It can also be used for inter-process communication via shared memory.
  3. Heap: Used for dynamic memory allocation at runtime. The heap grows upwards.
  4. Data Segment: Stores global and static data.
  5. Code Segment: Contains executable code and read-only constants.

Dynamic Memory Management in C: malloc/calloc/realloc/free

void Test()
{
    int* p1 = (int*)malloc(sizeof(int));
    free(p1);

    // What is the difference between malloc, calloc, and realloc?
    int* p2 = (int*)calloc(4, sizeof(int));
    int* p3 = (int*)realloc(p2, sizeof(int) * 10);
    // Do we need to free(p2) here?
    free(p3);
}

C++ Memory Management

C memory management can still be used in C++, but it has limitations and can be cumbersome. Therefore, C++ introduces its own memory management using new and delete operators.

1. new and delete for Built-in Types

void Test()
{
    // Dynamically allocate a single int
    int* ptr4 = new int;

    // Dynamically allocate a single int initialized to 10
    int* ptr5 = new int(10);

    // Dynamically allocate an array of 3 ints
    int* ptr6 = new int[3];

    delete ptr4;
    delete ptr5;
    delete[] ptr6;
}

new and delete diagram

Note: Use new and delete for single-element allocation/deallocation, and new[] and delete[] for arrays. Ensure they are used in matching pairs.

2. new and delete for User-Defined Types

#include <iostream>
using namespace std;

class A
{
public:
    A(int a = 0)
        : _a(a)
    {
        cout << "A():" << this << endl;
    }

    ~A()
    {
        cout << "~A():" << this << endl;
    }

private:
    int _a;
};

int main()
{
    // The key difference between new/delete and malloc/free is that new/delete
    // call the constructor and destructor for user-defined types.

    A* p1 = (A*)malloc(sizeof(A));   // Only allocates memory
    A* p2 = new A(1);                // Allocates memory and calls constructor

    free(p1);                         // Only frees memory
    delete p2;                        // Calls destructor and frees memory

    // For built-in types, they behave almost identically
    int* p3 = (int*)malloc(sizeof(int));
    int* p4 = new int;
    free(p3);
    delete p4;

    A* p5 = (A*)malloc(sizeof(A) * 10);
    A* p6 = new A[10];
    free(p5);
    delete[] p6;

    return 0;
}

Note: new calls the constructor and delete calls the destructor for user-defined types, whereas malloc and free do not.

Differecnes Between Dynamic Memory Allocation in C and C++

1. For Built-in Types

There is no significant difference between new/delete and malloc/free when dealing with built-in types.

2. For User-Defined Types

#include <iostream>
using namespace std;

class Date
{
    friend ostream& operator<< (ostream& cout, const Date& d);

public:
    Date(int year = 2023, int month = 12, int day = 31)
    {
        _year = year;
        _month = month;
        _day = day;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date* p1 = (Date*)malloc(sizeof(Date));   // Error if no default constructor? No, but the object is not initialized
    Date* p2 = new Date;                       // Requires a default constructor

    free(p1);
    delete p2;
    return 0;
}

ostream& operator<< (ostream& cout, const Date& d)
{
    cout << d._year << " " << d._month << " " << d._day << endl;
    return cout;
}

When new allocates space for a user-defined type, it first calls operator new (which internally wraps malloc), and then calls the constructor to initialize the allocated memory.

When delete releases the memory of a user-defined type, it first calls the destructor, and then calls operator delete (which internally wraps free) to destroy the space.

operator new and operator delete are global functions.

  • A* p1 = (A*)malloc(sizeof(A));p1 is of a built-in type and will not automatically envoke the constructor or destructor.

To explicitly call the constructor and destructor on memory allocated with malloc:

// Placement new: call constructor
new(p1) A;

// Explicit destructor call
p1->~A();

Tags: C++ C Memory Management new delete

Posted on Wed, 06 May 2026 10:38:13 +0000 by nogginj