Back to TILs

C++ heap vs stack

Date: 2021-01-03Last modified: 2024-11-02

Table of contents

Stack vs Pile vs Heap
Fig. 1 - Stack vs Pile vs Heap

Memory structure

Memory structure.
Fig. 2 - Memory structure.
Address space
Fig. 3 - Address space

Stack vs Heap Pros and Cons

Stack

Heap

Dynamic Memory Allocation in C

C provides the following functions to allocate memory at runtime:

Memory Allocation

Memory allocation.
Fig. 4 - Memory allocation.
Memory allocation.
Fig. 5 - Memory allocation.

@endpikchr

Example of use

  int a1;  // stack
  int a2;  // stack
  int a3;  // stack
  int a4;  // stack

  int *p1 = new int[5];  // heap
  int *p2 = new int[4];  // heap
  int *p3 = new int[3];  // heap
  int *p4 = new int[2];  // heap
  int *p5 = new int[1];  // heap; until p5 takes the next 32 bytes block
  int *p6 = new int[7];  // heap; long jump here!!!

  cout << &a1 << " stack a1\n";
  cout << &a2 << " stack a2: " << long(&a2) - long(&a1) << " bytes from a1\n";
  cout << &a3 << " stack a3: " << long(&a3) - long(&a2) << " bytes from a2\n";
  cout << &a4 << " stack a4: " << long(&a4) - long(&a3) << " bytes from a3\n";

  cout << p1 << " heap p1\n";
  cout << &p1[0] << " heap p1[0]\n";
  cout << &p1[1] << " heap p1[1]\n";
  cout << &p1[2] << " heap p1[2]\n";
  cout << &p1[3] << " heap p1[3]\n";
  cout << &p1[4] << " heap p1[4]\n";
  cout << p2 << " heap p2: " << long(p2) - long(p1) << " bytes from p1\n";
  cout << &p2[0] << " heap p2[0]\n";
  cout << &p2[1] << " heap p2[1]\n";
  cout << &p2[2] << " heap p2[2]\n";
  cout << &p2[3] << " heap p2[3]\n";
  cout << p3 << " heap p3: " << long(p3) - long(p2) << " bytes from p2\n";
  cout << p4 << " heap p4: " << long(p4) - long(p3) << " bytes from p3\n";
  cout << p5 << " heap p5: " << long(p5) - long(p4) << " bytes from p4\n";
  cout << p6 << " heap p6: " << long(p6) - long(p5) << " bytes from p5\n";

  delete[] p1;
  delete[] p2;
  delete[] p3;
  delete[] p4;
  delete[] p5;
  delete[] p6;

  // The pointer itself is allocated on the stack
  // The dynamic memeory is allocated on the heap
  // Typical metadata = size & allocation status
  // If the size is aligned to > 2 bytes,
  // can use bottom bit of size for allocated bit;
  auto printHeader = [](auto size) {
    int *ptr = static_cast<int *>(calloc(size, sizeof(int)));  // heap
    size_t * headerBytePtr = (size_t *)ptr;
    headerBytePtr--;
    fmt::print("{:16b} {:6d} {:6d} {:6d}\n", *headerBytePtr, *headerBytePtr, size * sizeof(int), size);
    free(ptr);
  };

  fmt::print("sizeof(size_t) = {} bytes\n", sizeof(size_t));
  for (int i = 1; i <= 32; ++i) {
    printHeader(i);
  }
  printHeader(100);
  printHeader(200);
  printHeader(1000);
  printHeader(10000);

Possible output

0x7ffe04b109d4 stack a1
0x7ffe04b109d8 stack a2: 4 bytes from a1
0x7ffe04b109dc stack a3: 4 bytes from a2
0x7ffe04b109e0 stack a4: 4 bytes from a3
0x55d218ea4eb0 heap p1
0x55d218ea4eb0 heap p1[0]
0x55d218ea4eb4 heap p1[1]
0x55d218ea4eb8 heap p1[2]
0x55d218ea4ebc heap p1[3]
0x55d218ea4ec0 heap p1[4]
0x55d218ea4ed0 heap p2: 32 bytes from p1
0x55d218ea4ed0 heap p2[0]
0x55d218ea4ed4 heap p2[1]
0x55d218ea4ed8 heap p2[2]
0x55d218ea4edc heap p2[3]
0x55d218ea4ef0 heap p3: 32 bytes from p2
0x55d218ea4f10 heap p4: 32 bytes from p3
0x55d218ea4f30 heap p5: 32 bytes from p4
0x55d218ea4f50 heap p6: 32 bytes from p5
sizeof(size_t) = 8 bytes
          100001     33      4      1
          100001     33      8      2
          100001     33     12      3
          100001     33     16      4
          100001     33     20      5
          100001     33     24      6
          110001     49     28      7
          110001     49     32      8
          110001     49     36      9
          110001     49     40     10
         1000001     65     44     11
         1000001     65     48     12
         1000001     65     52     13
         1000001     65     56     14
         1010001     81     60     15
         1010001     81     64     16
         1010001     81     68     17
         1010001     81     72     18
         1100001     97     76     19
         1100001     97     80     20
         1100001     97     84     21
         1100001     97     88     22
         1110001    113     92     23
         1110001    113     96     24
         1110001    113    100     25
         1110001    113    104     26
        10000001    129    108     27
        10000001    129    112     28
        10000001    129    116     29
        10000001    129    120     30
        10010001    145    124     31
        10010001    145    128     32
       110100001    417    400    100
      1100110001    817    800    200
    111110110001   4017   4000   1000
1001110001010001  40017  40000  10000

References