int a = 2;

0x100 2 a

La caja representa el espacio ocupado por la variable en el stack.

int a[3] = {1, 2, 3};

0x100
1 2 3
*(a + 0) *(a + 1) *(a + 2)

La caja representa el array y cada división representa uno de sus tres elementos.

int a = 2;
int* p = &a;

0x100 2 *p a
0x104 0x100 p

El puntero p almacena la dirección de memoria de a.

Estado actual: a = 2 y *p = 2.

int* p = new int(5);

STACK

0x104 0x200 p

HEAP

0x200 5 *p

En el stack, p guarda la dirección del valor reservado en el heap.

Heap activo: el valor `5` existe.

int* p = new int[3]{1, 2, 3};

STACK

0x104 0x300 p

HEAP

0x300
1 2 3
*(p + 0) *(p + 1) *(p + 2)

El puntero p guarda la dirección del primer elemento de un bloque contiguo de tres valores.

Array activo: los tres valores existen.

struct Punto {
  int x;
  int y;
};
Punto p;

p

El casillero de p se divide en dos campos internos: x e y.

int sumar(int a, int b) {
  return a + b;
}

sumar(2, 3);

0x0F4 sumar(2, 3) 0x0F8 2 a 0x0FC 3 b
Marco activo: a = 2 y b = 3; retorno aún no calculado.

struct Nodo {
  int valor;
  Nodo* siguiente;
};
Nodo* cabeza = new Nodo{10, nullptr};
cabeza->siguiente = new Nodo{20, nullptr};

STACK

0x104 0x300 cabeza

HEAP

0x300 10 0x320 nodo 1
0x320 20 nullptr nodo 2

cabeza guarda la dirección del primer nodo; cada nodo contiene un valor y la dirección del siguiente.

int factorial(int n) {
  if (n == 1) return 1;
  return n * factorial(n - 1);
}

factorial(3);

0x400 n = 3 ret = ? factorial(3)
0x410 n = 2 ret = ? factorial(2)
0x420 n = 1 ret = 1 factorial(1)

Cada llamada recursiva crea un nuevo marco en el stack; cuando llega a n = 1, los marcos regresan en sentido inverso.