Back to TILs

C++ heap vs stack

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

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

0x7ffc73f14f0c stack a1
0x7ffc73f14f08 stack a2: -4 bytes from a1
0x7ffc73f14f04 stack a3: -4 bytes from a2
0x7ffc73f14f00 stack a4: -4 bytes from a3
0x55f437da81c0 heap p1
0x55f437da81c0 heap p1[0]
0x55f437da81c4 heap p1[1]
0x55f437da81c8 heap p1[2]
0x55f437da81cc heap p1[3]
0x55f437da81d0 heap p1[4]
0x55f437da81e0 heap p2: 32 bytes from p1
0x55f437da81e0 heap p2[0]
0x55f437da81e4 heap p2[1]
0x55f437da81e8 heap p2[2]
0x55f437da81ec heap p2[3]
0x55f437da8200 heap p3: 32 bytes from p2
0x55f437da8220 heap p4: 32 bytes from p3
0x55f437da8240 heap p5: 32 bytes from p4
0x55f437d9eda0 heap p6: -38048 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
         1010001     81     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
         1110001    113     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