• Save
Gestione della memoria in C++
Upcoming SlideShare
Loading in...5
×
 

Gestione della memoria in C++

on

  • 409 views

Introduzione al concetto di oggetto nel modello della memoria del C++ e ai suoi possibile tempi di vita (temporaneo, automatico, dinamico, ...). Relazione tra il tempo di vita e la visibilità (scope) ...

Introduzione al concetto di oggetto nel modello della memoria del C++ e ai suoi possibile tempi di vita (temporaneo, automatico, dinamico, ...). Relazione tra il tempo di vita e la visibilità (scope) di un oggetto. Gestione degli oggetti dinamici per tipi primitivi, strutture e array mediante l'utilizzo di puntatori (raw pointers).

Statistics

Views

Total Views
409
Views on SlideShare
390
Embed Views
19

Actions

Likes
1
Downloads
0
Comments
0

3 Embeds 19

http://efesto.cloudapp.net 11
http://www.giuseppesportelli.it 5
http://lare.cloudapp.net 3

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Gestione della memoria in C++ Gestione della memoria in C++ Presentation Transcript

  • Gestione della memoria Ilio Catallo – Politecnico di Milano ilio.catallo@polimi.it
  • Outline ¤  Tempo di vita degli oggetti ¤  Segmentazione della memoria ¤  Oggetti dinamici ¤  Strutture dinamiche ¤  Array dinamici ¤  Array monodimensionali ¤  Array multidimensionali 2
  • Tempo di vita degli oggetti 3
  • Oggetti (1) Un oggetto è una regione di memoria di un determinato tipo dato ¤  Una variabile può dunque essere definita come un oggetto con nome ¤  RICORDA: il termine oggetto può anche essere usato come sinonimo di istanza di una classe ¤  Le due definizione non sono equivalenti 4
  • Oggetti (2) ¤  Esempio: oggetto senza nome (temporaneo) di tipo int! std::cout << (5+3)*3 << std::endl; ¤  Esempio: oggetto con nome (variabile) di tipo int! int result = (5+3)*3; // ‘result’ is the name of // an object of type int std::cout << result << std::endl; 5
  • Tempo di vita di un oggetto (1) ¤  Ogni oggetto in un programma è caratterizzato da un proprio tempo di vita (storage duration) ¤  Tempo di vita: l’intervallo di tempo che intercorre tra la creazione dell’oggetto e la sua distruzione ¤  Perchè limitare il tempo di vita degli oggetti? ¤  Per preservare spazio in memoria ¤  Esempio: se una funzione non è in esecuzione, non c’è motivo di mantenere in memoria le corrispettive variabili locali 6
  • Tempo di vita di un oggetto (2) ¤  A seconda del suo tempo di vita, un oggetto può essere classificato come: ¤  Temporaneo ¤  Automatico ¤  Statico ¤  Dinamico ¤  Thread-local 7
  • Oggetti temporanei (1) ¤  Gli oggetti temporanei contengono risultati intermedi ottenuti durante la valutazione di un’epressione int v = x + y*z; ¤  Esempio: Il risultato di y*z deve essere salvato da qualche parte prima di poter essere aggiunto a x! 8
  • Oggetti temporanei (2) int v = x + y*z; ¤  L’oggetto temporaneo non ha un nome ¤  La loro creazione è invisibile all’utente ¤  Non è possibile assegnargli un valore ¤  L’oggetto viene distrutto al termine della full-expression che lo contiene ¤  Full-expression: un’espressione che non è contenuta in un’espressione più grande 9
  • Oggetti automatici (1) ¤  Un oggetto automatico viene creato al momento della sua definizione e distrutto al termine del blocco che lo contiene ¤  Gli oggetti automatici più comuni sono le variabili locali void do_nothing() { int x = 12; } ¤  Esempio: la variabile locale x viene distrutta al termine del blocco, ed è quindi un oggetto automatico 10
  • Oggetti automatici (2) una variabile locale è un oggetto automatico ¤  Attenzione a non confendere i due concetti: ¤  Il nome della variabile è locale: il nome è visibile unicamente all’interno del blocco ¤  L’oggetto è automatico: la variabile verrà distrutta al termine del blocco ¤  Le variabili locali non sono gli unici oggetto automatici ¤  Anche i parametri d’ingresso ad una funzione sono oggetti automatici 11
  • Oggetti statici (1) ¤  Un oggetto statico viene create ad avvio del programma e distrutta al termine del programma ¤  Esempi di oggetti statici: ¤  Variabili globali ¤  Variabili locali dichiarate come static! 12
  • Oggetti statici (2) ¤  Una variabile globale è una variabile il cui nome è visibile in ogni punto del programma int x = 10; int main() { x = 5; // 'x' is a global variable std::cout << x << std::endl; } ¤  La variabile globale x è un oggetto statico, la sua vita termina al termine del programma 13
  • Oggetti statici (3) ¤  Una variabile locale static è inizializzata a zero ad avvio del programma, e distrutta al termine dell’applicativo size_t count_calls() { static size_t ctr; return ++ctr; } ¤  Esempio: la variabile ctr viene incrementata ad ogni chiamata di count_calls()! 14
  • Oggetti statici (4) size_t count_calls() { static size_t ctr; return ++ctr; } ¤  il blocco che contiene una variabile locale static determina la visibilità della variabile, ma non il suo tempo di vita ¤  Il nome della variabile è visibile solo all’interno della funzione, ma… ¤  La variabile viene distrutta al termine del programma, non al termine della funzione 15
  • Oggetti dinamici (1) ¤  Una volta creato, un oggetto dinamico rimane in memoria fino a quando non viene esplicitamente richiesta la sua distruzione ¤  Il tempo di vita di un oggetto dinamico è gestito dal programmatore, che decide: ¤  Quando creare l’oggetto dinamico ¤  Quando distruggere l’oggetto 16
  • Oggetti dinamici (2) int Il programmatore crea un oggetto dinamico di tipo int! 5! int L’oggetto dinamico viene utilizzato! 5! int Il programmatore distrugge l’oggetto quando non più necessario! 17
  • Segmentazione della memoria 18
  • Processo in memoria (1) ¤  Il sistema operativo (SO) assegna ad ogni programma in esecuzione (processo) una porzione della memoria disponibile ¤  Tale memoria permette l’effettiva esecuzione del programma ¤  Esempio: una parte viene adoperata per mantenere in memoria il valore delle variabili 19
  • Processo in memoria (2) 1775! …! 3472! memoria assegnata al programma in esecuzione! ¤  La porzione di memoria dedicata al processo viene chiamata lo spazio d’indirizzamento (address space) del processo 20
  • Segmentazione (1) ¤  Lo spazio d’indirizzamento di un processo è suddiviso in quattro aree (segmenti): ¤  Code segment* ¤  Data segment ¤  Heap segment ¤  Stack segment ¤  Ogni segmento viene utilizzato per uno scopo diverso *anche detto text segment 21
  • Segmentazione (2) 1775! Code! …! Data! Heap! 3472! Stack! memoria assegnata al programma in esecuzione! ¤  La classica rappresentazione dello spazio d’indirizzamento è ottenuta semplicemente ruotando la figura di 90° 22
  • 3472! Stack Heap …! Data Code memoria assegnata al programma in esecuzione! Segmentazione (3) 1775! 23
  • Code segment ¤  Il code segment è un’area di memoria a sola lettura che contiene il codice che verrà eseguito int main() { Stack int a = 12, b = 13; int c = a + b; Heap } Data movl movl movl $12, -12(%rbp) $13, -16(%rbp) -12(%rbp), %eax 24
  • Data segment ¤  Il data segment contiene variabili globali e variabili locali static ! int x = 10; size_t count_calls() { static size_t ctr; return ++ctr; } int main() { count_calls(); return 0; } Stack Heap x: 10! ctr: 0! Code 25
  • Stack segment (1) ¤  Lo stack mantiene in memoria le variabili locali ed i parametri in ingresso alle funzioni int main() { int a = 15, b = 10; return 0; } a: 15! b: 10! spazio rimanente nello stack Heap Data Code 26
  • Stack segment (2) ¤  Lo stack è organizzato come una pila, ogni volta che una funzione viene invocata, viene aggiunto un nuovo strato ¤  Il nuovo strato (frame) contiene le variabili locali ed i parametri in ingresso della funzione appena invocata* ¤  Quando una funzione termina, il corrispettivo frame viene rimosso dalla pila ¤  Le variabili locali vengono automaticamente distrutte * il frame contiene anche informazioni che permettono, una volta termina una funzione, che il controllo ritorni alla funzione chiamante 27
  • Stack segment (3) void do_nothing2() { int y = 13; } void do_nothing1() { int x = 12; do_nothing2(); } int main() { int a = 15, b = 10; do_nothing1(); return 0; } a: 15 main()! b: 10 x: 12 do_nothing1()! y:13 do_nothing2()! spazio rimanente nello stack Frame 1! Frame 2! Frame 3! 28
  • Heap segment ¤  L’heap segment è l’area di memoria che contiene gli oggetti dinamici ¤  A differenza dello stack, un nuovo oggetto può occupare una posizione casuale all’interno dell’heap Occupato! Libero! Heap! ¤  Conseguenza: non esiste nessun meccanismo che automaticamente elimini gli oggetti non più utilizzati 29
  • Oggetti dinamici 30
  • Creare oggetti dinamici (1) Come possiamo creare un oggetto dinamico? ¤  Siamo al corrente di un solo modo per creare un oggetto che sia successivamente modificabile: ¤  Assegnargli un nome, in altre parole definire una variabile ¤  Assegnare un nome ad un oggetto comporta però la definizione del suo tempo di vita ¤  Variabile locale? oggetto automatico ¤  Variabile locale static? oggetto statico ¤  variabile globale? oggetto statico 31
  • Creare oggetti dinamici (2) ¤  Un oggetto dinamico deve quindi essere anonimo, in altre parole, non deve avere un nome Come possiamo manipolare un oggetto senza nome? ¤  Anche se senza nome, l’oggetto dinamico deve avere un indirizzo ¤  IDEA: usare un puntatore per contenere l’indirizzo dell’oggetto dinamico 32
  • Espressione new (1) ¤  L’espressione new permette di creare oggetti dinamici int* a = new int; // 'a' is pointing to an unnamed, // dynamic object of type int ¤  Notare come: ¤  a sia una variabile locale, dunque un oggetto automatico ¤  l’oggetto int anonimo sia invece un oggetto dinamico ¤  Ricorda: la variabile a non è l’oggetto dinamico, è un puntatore all’oggetto dinamico 33
  • Rappresentazione in memoria Un oggetto dinamico non ha nome a: int int* Heap! Stack! 34
  • Espressione new (2) int* a = new int; ¤  L’espressione new esegue due operazioni: ¤  Invoca l’operatore new, che alloca nell’heap il quantitativo di memoria necessaria ¤  Costruisce l’oggetto nell’area di memoria designata ¤  L’espressione new restituisce l’indirizzo dell’oggetto dinamico 35
  • Inizializzare oggetti dinamici (1) ¤  Gli oggetti dinamici sono inizializzati a default ¤  I tipi built-in contengono quindi un valore non noto ¤  Istanze di una classe vengono inizializzati attraverso il costruttore di default ¤  Come per le variabili, è quindi buona norma inizializzare gli oggetti dinamici con valori sensati 36
  • Inizializzare oggetti dinamici (2) int b = 20; int* d = new int(12); // direct initialization int* c = new int(b); // copy initialization int* e = new int(); // value initialization ¤  Sono possibili diversi tipi di inizializzazione: ¤  Inizializzazione diretta: l’oggetto puntato da d vale 12! ¤  Inizializzazione per copia: l’oggetto puntato da c vale 20! ¤  Inizializzazione per valore: l’oggetto puntato da e vale 0! 37
  • Modificare oggetti dinamici ¤  Per alterare oggetto dinamici si utilizza l’operatore di indirezione *! int* a = new int; int b = 12; *a = b + 7; std::cout << "the value pointed to by a is ” << *a << std::endl; } 38
  • Distruggere oggetti dinamici ¤  Gli oggetti dinamici vanno esplicitamente distrutti ¤  Ogni oggetto allocato occupa parte della memoria disponibile nell’heap ¤  La memoria non è illimitata ¤  È tecnicamente possibile saturare tutto lo spazio a disposizione 39
  • Espressione delete (1) ¤  L’espressione delete permette di distruggere un oggetto dinamico int* a = new int; int b = 12; *a = b + 7; delete a; ¤  Dopo la delete, l’oggetto dinamico puntato da a non esiste più 40
  • Espressione delete (2) 19 a: int* int Heap! Stack! 41
  • Espressione delete (3) delete a; ¤  L’espressione delete esegue due operazioni: ¤  Invoca l’operatore delete, così da distrugge l’oggetto ¤  Dealloca la memoria precedentemente allocata, rendendola così nuovamente disponibile 42
  • Dangling pointers (1) ¤  L’espressione delete distrugge l’oggetto dinamico ¤  Il corrispettivo puntatore non è quindi più valido ¤  Infatti, l’oggetto puntato non esiste più ¤  Il puntatore continua però a contenere l’indirizzo di dove una volta si trovava l’oggetto dinamico ¤  Il puntatore è diventato un dangling pointer ¤  Dangling pointer: puntatore che punta ad un area di memoria precedentemente occupata da un oggetto non più esistente 43
  • Dangling pointers (2) ¤  La soluzione è quindi rafforzare il fatto che il puntatore non sta più puntando a nulla ¤  Regola (del pollice): dopo la distruzione di un oggetto dinamico, il corrispettivo puntatore deve essere posto a nullptr (o NULL) delete a; a = nullptr; 44
  • Strutture dinamiche 45
  • Strutture (1) Una struttura è una sequenza di elementi (membri) di tipo arbitrario ¤  Le strutture generalizzano il concetto di array: ¤  Un array è una sequenza di elementi dello stesso tipo ¤  Una struttura è un sequenza di elementi di tipo diversi 46
  • Strutture (2) struct Address { std::string street; int number; std::string town; }; ¤  Address è un tipo composto e definito dall’utente ¤  Una volta che il tipo Address è stato definito, è possibile dichiarare variabili di tipo Address: Address polimi_address; 47
  • Inizializzare una struttura ¤  Così come per gli array, è possibile inizializzare oggetti di tipo struttura mediante una lista di inizializzazione* Address polimi_address = {"Piazza Leonardo Da Vinci", 32, "Milan"}; *ciò è in realtà possibile unicamente se la struttura è anche un aggregato 48
  • Member access operator ¤  I membri di una struttura sono accessibili mediante l’operatore . (member access operator) polimi_address.street = "Piazza Leonardo Da Vinci"; polimi_address.number = 32; polimi_address.town = "Milan”; 49
  • Strutture dinamiche ¤  È possibile dichiarare strutture dinamiche mediante l’espressione new! Address* polico_address = new Address; // default // initialization ¤  L’espressione new restituisce l’indirizzo della struttura dinamica allocata nell’heap ¤  La struttura polico_address è inizializzata a default 50
  • Rappresentazione in memoria Un struttura dinamica non ha nome street: Address number: town: polico_address: Address* Heap! Stack! 51
  • Inizializzare una struttura dinamica (2) ¤  In C++11, una struttura dinamica può essere inizializzata con una lista di inizializzazione* Address* polico_address = new Address{“Via Valleggio", 11, ”Como"};// uniform // initialization *ciò è in realtà possibile unicamente se la struttura è anche un aggregato 52
  • Accedere a membri di una struttura dinamica (1) ¤  Per accedere alla struttura dinamica è necessario: ¤  Accedere alla struttura mediante l’operatore *! ¤  Accedere a un particolare membro mediante l’operatore .! ! (*polico_address).street = "Via Valleggio”; Mediante indirezione recuperiamo la struttura Mediante l’operatore di accesso alteriamo un particolare membro della struttura 53
  • Accedere a membri di una struttura dinamica (2) ¤  Per accessi a membri di una struttura mediante puntatore è disponibile una variante del member access operator polico_address->street = "Via Valleggio”; ¤  L’operatore -> è talvolta indicato come pointer-to-member operator 54
  • Distruggere una struttura dinamica ¤  Una struttura dinamica si distrugge mediante l’utilizzo della espressione delete! delete polico_address; 55
  • Array dinamici
  • Creare array dinamici (1) ¤  Un array dinamico può essere creato mediante l’espressione new[]! int* numbers = new int[5]; // dynamic (hence, unnamed) array // of 5 elements of type int ¤  L’espressione new restituisce l’indirizzo del primo elemento dell’array (non avviene alcun decadimento) ¤  Nell’esempio, tale indirizzo viene salvato in un puntatore di nome numbers! 57
  • Creare array dinamici (2) Un array dinamico non ha nome (array anonimo) numbers: int[5] int* Heap! Stack! 58
  • Inizializzare un array dinamico ¤  In C++11, un array dinamico può essere inizializzato in in modo simile ad un comune array: int* numbers = new int[5]{1, 7, 13, 5, 9}; ¤  Tale modalità di inizializzazione si chiama inizializzazione uniforme (uniform initialization) 59
  • Accedere ad un array dinamico ¤  L’accesso ad un array dinamico avviene secondo le stesse modalità di un comune array int* numbers = new int[5]{1, 7, 13, 5, 9}; numbers[2] = 20; // implemented as *(numbers + 2) = 20 ¤  Infatti, come sappiamo l’operatore [] è un operatore definito sui puntatori 60
  • Distruggere un array dinamico ¤  Il programmatore è responsabile della distruzione di array dinamici non più necessari ¤  L’espressione delete[] distrugge un array dinamico! delete[] numbers; ¤  L’uso delle [] è cruciale ¤  È l’unico modo per chiedere al compilatore di distruggere un array di oggetti, piuttosto che un singolo oggetto ¤  Se non usato, si incappa in un undefined behavior 61
  • Array di dimensioni variabili
  • Dimensione di un array ¤  La dimensione di un array automatico deve essere nota a tempo di compilazione (compile time) constexpr size_t DIM = 5; int numbers[DIM]; ¤  Tale imposizione facilita la gestione della stack segment 63
  • Dimensione di un array vs stack ¤  Invocare una funzione significa impilare un nuovo frame nello stack segment void do_nothing() { int x = 2; char y[2] ={5, 4}; int c = 3; } int main() {
 int a = 12;
 do_nothing();
 return 0;
 }" a: 12 main()! x: 2 y[0]: 5 y[1]: 4 do_nothing()! c: 3 spazio rimamente nello stack 64
  • Frame pointer e stack pointer (1) ¤  Durante l’esecuzione di una funzione, possiamo identificare due indirizzi importanti in memoria ¤  Stack pointer: l’indirizzo dell’ultima cella di memoria valida nello stack ¤  Frame pointer: l’indirizzo della prima cella di memoria appartenente al frame attualmente in esecuzione 65
  • Frame pointer e stack pointer (2) ¤  Se supponiamo che do_nothing() sia in esecuzione, si ha che: a: 12 frame pointer! main()! x: 2 y[0]: 5 y[1]: 4 stack pointer! do_nothing()! c: 3 spazio rimamente nello stack 66
  • Frame pointer e stack pointer (3) ¤  Gli indirizzi delle variabili locali della funzione in esecuzione sono deducibili a partire dall’indirizzo contenuto nel frame pointer indirizzo var. locale = frame pointer + spiazzamento ¤  Dove lo spiazzamento è misurato in byte 67
  • Frame pointer e stack pointer (4) ¤  Se supponiamo che un int occupi 2 byte, e un char 1 byte a: 12 frame pointer! x: 2 frame pointer + 0! y[0]: 5 y[1]: 4 stack pointer! frame pointer + 2 byte! frame pointer + 3 byte! c: 3 frame pointer + 4 byte! spazio rimamente nello stack 68
  • Dimensione di un array ¤  Se richiediamo che le variabili abbiano dimensioni note a priori tutti gli spiazzamenti sono calcolabili a compile-time ¤  Conseguenza: è possibile conoscere a tempo di compilazione la dimensione del frame di una funzione ¤  Questo comporta: ¤  Codice più veloce ¤  Maggiore semplicità nella scrittura dei compilatori 69
  • Array di dimensioni variabili (1) Come definire un array la cui dimensione venga specificata a tempo d’esecuzione (run time)? ¤  Non possiamo usare normali array, perchè verrebbero allocati nello stack, totalmente gestito dal compilatore ¤  Al contrario, l’heap è una porzione di memoria totalmente a disposizione del programmatore ¤  IDEA: memorizzare l’array di dimensioni variabili all’interno dell’heap segment 70
  • Array di dimensioni variabili (2) ¤  Al momento della creazione di un array dinamico, la dimensione può essere una qualsiasi espressione ¤  Non è richiesto che l’espressione sia costante size_t dim = 5; int* numbers = new int[dim]; ¤  Nell’esempio, dim non è una quantità costante, ma può comunque essere usata per creare un array dinamico 71
  • Array dinamici multidimensionali 72
  • Array dinamici multidim. (1) ¤  È possibile creare un array dinamico multidimensionale attraverso l’espressione new[] int (*matrix)[3] = new int[3][3]; ¤  L’espressione new[] restituisce l’indirizzo del primo elemento dell’array multidim. ¤  Gli elementi di un un array multidim. sono a loro volta array ¤  Un puntatore al primo elemento è un puntatore ad array 73
  • Array dinamici multidim. (2) int[3][3] ! matrix: int(*)[3]" Heap! Stack! 74
  • Alias per i sotto-array ¤  È possibile aumentare la leggibilità del codice definendo un alias per il tipo “riga della matrice” typedef int matrix_row[3]; matrix_row* matrix = new int[3][3]; ¤  La scrittura evidenzia come la new[] restituisca un puntatore alla prima riga della matrice 75
  • Nascondere il tipo dei sotto-array ¤  In C++11, è possibile far sì che sia il compilatore a dedurre il tipo dato di una variabile definendo la variabile come auto! ¤  Tale funzionalità può essere usata per aumentare la leggibilità* auto matrix = new int[3][3]; *L’utilizzo della keyword auto andrebbe limitato ai soli casi in cui è palese il tipo dato che verrà dedotto per la variabile 76
  • Inizializzare array dinamici multidimensionali ¤  In C++11, è possibile inizializzare un array dinamico multidim. in maniera simile ad un normale array multidim. auto matrix = new int[3][3]{{1, 7, 14}, {8, 16, 12}, {27, 32, 5}}; 77
  • Distruggere array dinamici multidimensionali ¤  Così come per i normali array dinamici, l’espressione delete[] consente di distruggere array multidimensionali delete[] matrix; 78
  • Array dinamici multidim. di dimensione variabile ¤  Tutte le dimensioni dell’array, con la sola eccezione della prima, devono essere note a tempo di compilazione size_t N = 3; auto matrix = new int[N][3] // OK auto matrix2 = new int[3][N] // ERROR! ¤  matrix è un array dinamico di N elementi di tipo int[3]! ¤  Essendo dinamico, N può essere noto a run time ¤  Il tipo dell’elemento (int[3]) deve però essere noto a compile time 79
  • Bibliografia 80
  • Bibliografia (1) ¤  S. B. Lippman, J. Lajoie, B. E. Moo, C++ Primer (5th Ed.) ¤  B. Stroustrup, The C++ Programming Language (4th Ed.) ¤  S. Meyers, Effective C++ (2nd Ed.) ¤  R. Lafore, Object Oriented Programming in C++ (4th Ed.) 81
  • Bibliografia (2) ¤  C++ Reference, Zero initialization http://en.cppreference.com/w/cpp/language/ zero_initialization ¤  C++ Reference, Value initialization http://en.cppreference.com/w/cpp/language/ value_initialization ¤  C++ Reference, new expression http://en.cppreference.com/w/cpp/language/new ¤  C++ Reference, delete expression http://en.cppreference.com/w/cpp/language/delete 82
  • Bibliografia (3) ¤  Wikipedia, Data Segment http://en.wikipedia.org/wiki/Data_segment ¤  University of Alberta, Understanding memory http://www.ualberta.ca/CNS/RESEARCH/LinuxClusters/ mem.html ¤  G. Duarte, Anatomy of a program in memory http://duartes.org/gustavo/blog/post/anatomy-of-aprogram-in-memory 83
  • Bibliografia (4) ¤  Stackoverflow FAQ, “How do I use arrays in C++?” http://stackoverflow.com/questions/4810664/how-do-iuse-arrays-in-c ¤  Stackoverflow FAQ, “What are Aggregates and PODs and how/why are they special?” http://stackoverflow.com/q/4178175/1849221 84