Utilizza l'allocazione della memoria Stack vs Heap in C++

Jinku Hu 12 ottobre 2023
Utilizza l'allocazione della memoria Stack vs Heap in C++

Questo articolo spiegherà diversi metodi su come utilizzare l’allocazione della memoria stack rispetto all’heap in C++.

Differenza tra memoria stack e memoria heap in C++

Quando vogliamo discutere i concetti di memoria, è meglio pensare in termini di sistemi in cui vengono eseguiti i programmi utente più comuni. La maggior parte dei programmi utente viene eseguita in un ambiente del sistema operativo, che gestisce le risorse hardware per noi e si occupa di varie attività che sarebbero troppo complesse o inefficienti da gestire per un programma utente. Uno di questi compiti è gestire direttamente la memoria hardware. Pertanto, quasi tutti i sistemi operativi forniscono strutture e funzioni speciali per interagire con la memoria hardware. Due concetti comuni nelle strutture di memoria fornite dal sistema operativo sono stack e heap.

Uno stack è una regione di memoria riservata a ciascun programma in esecuzione nel sistema e opera in modo LIFO. Vale a dire, quando il programma avvia l’esecuzione della funzione main, quest’ultima riceve il suo stack frame (un sottoinsieme della memoria dello stack), in cui vengono memorizzati automaticamente le variabili locali e gli indirizzi di ritorno delle chiamate di funzione. Una volta che il main chiama un’altra funzione, viene creato un nuovo stack frame dopo il precedente in modo continuo. Il frame dello stack più recente memorizzerà gli oggetti locali per la funzione corrispondente e quando restituisce questa memoria non sarà occupata.

Si noti che la dimensione dello stack è fissa sulla maggior parte dei sistemi per impostazione predefinita, ma può essere personalizzata in una certa misura se l’utente ha esigenze speciali. La limitazione delle dimensioni della memoria dello stack lo rende adatto per oggetti piccoli e per lo più temporanei. Ad esempio, la dimensione dello stack predefinita per il programma utente nel sistema operativo Linux è 8 MB. Può essere più piccolo di una singola foto JPEG che un programma potrebbe dover elaborare, quindi l’utente deve utilizzare questo spazio con diligente cautela. Le variabili dichiarate nel frammento di codice seguente sono tutte archiviate nella memoria dello stack. Come regola generale, ogni variabile locale viene allocata nello stack se non ha specificatori speciali come static o volatile.

#include <iostream>

using std::cout;
using std::endl;

int main() {
  int var1;
  int var2 = 123;
  int arr1[4] = {1, 2, 3, 4};
  int var3 = var2;

  cout << var1 << endl;
  cout << var2 << endl;
  cout << var3 << endl;

  return EXIT_SUCCESS;
}

Produzione:

0
123
123

Esiste invece una regione di memoria denominata - heap (detta anche free store), in cui possono essere archiviati oggetti di grandi dimensioni e le allocazioni effettuate manualmente dal programmatore durante il runtime. Queste due caratteristiche rendono la memoria heap di natura dinamica, poiché la sua dimensione non deve essere determinata in fase di compilazione o in qualsiasi momento durante l’esecuzione del programma. Il programma può richiamare funzioni speciali e richiedere le allocazioni dal sistema operativo. Si noti che la memoria heap può sembrare infinita dal punto di vista del programma poiché non è limitato a chiamare un’altra funzione di allocazione per richiedere più memoria. Tuttavia, il sistema operativo gestisce la memoria per tutti i processi in esecuzione; e potrebbe rifiutare nuove allocazioni quando non è più disponibile memoria fisica.

Il sistema di memoria nel sistema operativo è piuttosto complesso e richiede la comprensione di vari concetti specifici del sistema operativo/hardware, quindi in questo argomento tratteremo solo più del minimo indispensabile sulle memorie heap e stack. La gestione manuale della memoria heap in linguaggio C++ può essere eseguita utilizzando gli operatori new/delete o le funzioni malloc/free. Si noti che queste funzioni funzionano in modo simile, in cui l’utente di solito specifica il numero di byte da allocare e restituisce l’indirizzo in cui è stata allocata la stessa quantità di memoria. Il programmatore può conseguentemente operare sulla data regione di memoria secondo necessità.

L’esempio di codice successivo illustra diversi casi di allocazione dei diversi oggetti nella memoria heap. Una caratteristica importante della gestione manuale della memoria è restituire la regione di memoria allocata al sistema operativo quando non è più necessaria. Quest’ultima operazione viene effettuata mediante chiamate delete/free corrispondenti alle controparti di allocazione. Se il programma non libera la memoria non necessaria, c’è il rischio che il sistema operativo esaurisca la memoria e di conseguenza il programma potrebbe essere terminato. Si noti, tuttavia, che il problema precedente dovrebbe verificarsi principalmente nei programmi di lunga durata e sono caratterizzati come bug di perdita di memoria.

#include <iostream>

using std::cout;
using std::endl;

int main() {
  auto var4 = new int;
  cout << var4 << endl;

  int *arr2 = new int[4];
  auto arr3 = new int[4];
  cout << arr2 << endl;
  cout << arr3 << endl;

  delete var4;
  delete[] arr2;
  delete[] arr3;
  return EXIT_SUCCESS;
}
Autore: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Articolo correlato - C++ Memory