User Tools

Site Tools


appunti3s:pointers

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
appunti3s:pointers [2019/06/29 15:33] – da eliminare... profproappunti3s:pointers [2020/06/08 22:19] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +Attenzione: questa pagina è stata ;;aggiornata;;, vedere: [[appunti3s:gestione_della_memoria]]
 +
 +====== Puntatori a... (qualcosa) ======
 +=====Memoria automatica=====
 +Le variabili automatiche (cosiddette locali) sono gestite automaticamente nello //stack// e quando non servono più vengono automaticamente "distrutte".
 +=====Memoria dinamica=====
 +vedere [[appunti3s:code_data_bss]]
 +
 +Durante l'esecuzione del programma può essere necessario allocare (riservare) dinamicamente della memoria. Questo accade per le operazioni dove non si possono prevedere in anticipo le richieste di un utente. 
 +Per riservare una zona di memoria dinamicamente si usa l'operatore //new//.
 +<code>new float; // riserva la quantità di memoria necessaria per un numero in virgola mobile</code>
 +L'operatore //new// restituisce l'indirizzo iniziale della memoria riservata.
 +Quale tipo di dato è utile per memorizzare un indirizzo di memoria? Anche se un indirizzo di memoria è un numero, non si usa il tipo //int//, ma un tipo specifico chiamato //puntatore a...//. Nel precedente esempio si deve usare un "puntatore a float". Per la sua dichiarazione basta aggiungere l'operatore asterisco * dopo il nome del tipo:
 +<code>
 +float* numero;       //definizione della variabile puntatore a...
 +numero = new float;  // inizializzazione della variabile puntatore a..
 +</code>
 +
 +{{:appunti3s:puntatore1.png?|}}
 +
 +Nel precedente esempio il programma ha riservato due aree di memoria:
 +  * "numero" come variabile //automatica//, per contenere un indirizzo
 +  * "new float" come variabile //dinamica//, per contenere un numero in virgola mobile
 +Le due operazioni possono essere eseguite anche in un solo colpo:
 +<code>float* numero = new float;</code>
 +La seconda variabile (nell'heap) sarebbe inutilizzabile senza la prima (nello stack), ma chi è che controlla la "distruzione" della prima? Poiché "numero" è una variabile automatica essa si libera automaticamente quando termina la funzione che la contiene, quindi l'area allocata dinamicamente potrebbe diventare inutilizzabile nel caso in cui questo accadesse. Nell'esempio visto, il programmatore deve usare //delete// prima che si verifichi questo evento "disastroso":
 +
 +<code>delete numero;</code>
 +
 +Riassumendo, questo è l'ordine in cui si devono fare le cose
 +  * inizia la funzione
 +    - definire un puntatore (nella mem. automatica)
 +    - riservare la memoria dinamica con //new// e inizializzare il puntatore
 +    - utilizzare a proprio piacere l'area dinamica...
 +    - liberare la memoria dinamica con //delete//
 +  * termina la funzione (si libera anche il puntatore)
 +Oppure, in altre parole, ricordarsi di liberare tutte le variabili dinamiche create per evitare problemi di gestione della memoria o falle (memory leak).
 +I puntatori quindi sono variabili usate prevalentemente per controllare la memoria dinamica (//heap//).
 +
 +====Un consiglio====
 +L'utilizzo dei puntatori è un comune causa di errori o bug nei programmi, soprattutto se questi sono utilizzati per un lungo tempo. La strategia consigliata è di usare l'operatore //new// solo nei costruttori e l'operatore //delete// solo nei distruttori.
 +
 +I puntatori e gli array lavorano sulla memoria a basso livello, cioè senza controlli, causando oscuri malfunzionamenti e bug! I vector invece hanno un'efficenza ancora sufficientemente elevata e sono più facili da usare (ci sono più controlli).
 +
 +Creando un vector (o uno fstream) come oggetto locale, la distruzione sarà eseguita automaticamente al termine della funzione e così si posso evitare i puntatori.
 +In ogni caso, non usare direttamente new e delete, ma creare oggetti con new nel costruttore e delete nel distruttore.
 +
 +===== Perché usare la memoria dinamica? =====
 +
 +La memoria dinamica, rispetto alla memoria automatica, può essere utile per contenere oggetti molto grandi o di dimensioni non note a priori, oppure per contenere oggetti che devono durare a lungo (più della durata degli oggetti locali (automatici) di una funzione).
 +===== Perché usare i puntatori? =====
 +I puntatori non vengono usati solo per accedere alla memoria heap.
 +A volte i puntatori permettono di accedere anche a zone della memoria (stack) altrimenti inaccessibili, come quando si vuole che una funzione conosca l'indirizzo di una variabile usata da un'altra per poterla modificare. Ma questo utilizzo non dovrebbe essere necessario e probabilmente si basa su un cattivo stile di programmazione.
 +A volte i puntatori permettono di puntare alle funzioni, consentendo di eseguire funzioni diverse per condizioni diverse che si possono verificare in tempo di esecuzione (running time), in altre parole i puntatori consentono anche il [[appunti3s:polimorfismo]]. 
 +http://www.illuminamente.org/dokuwiki/doku.php?id=appunti3s:stream#file
 +
 +===== Differenze tra puntatori e array =====
 +<code>int a1[5];</code>
 +In linguaggio C scrivere "a1" equivale a scrivere l'indirizzo del primo elemento dell'array "&a1[0]" , quindi, quando si passa un array ad una funzione, in realtà si passa sempre il valore del suo indirizzo (come un puntatore). 
 +
 +La differenza tra un puntatore (int p) è l'indirizzo (&a1[0]) è che il primo può cambiare e puntare qualsiasi area di memoria, mentre l'indirizzo del primo elemento di un array non può essere cambiato.
 +===== Similitudini tra puntatori e array =====
 +<code>int *p = a1</code>
 +Se il puntatore punta all'area di memoria di un array, si può utilizzare una sintassi simile a quella di un array:
 +<code>p[0]=3; </code>
 +
 +Nelle dichiarazioni delle funzioni si può usare equivalentemente una qualsiasidelle seguenti due sintassi:rif.standard  13.1-3.3
 +<code>int fun(int p[]);
 +int fun(int *p);</code>
 +
 +===== Puntatore appeso =====
 +differenza tra 
 +
 +<code>
 +return &variabile; // (dangling pointer) resituisce l'indirizzo di una 
 +                   // var che si autodistrugge(locale,automatica)</code>
 +<code>
 +return puntatore; // restituisce un indirizzo (la copia del contenuto di 
 +                  // un puntatore) che contiene l'indirizzo di (si spera) 
 +                  // un'area allocata con new, che sopravvive alla funzione.</code>
 +
 +===== Puntatore a void=====
 +È introdotto nel linguaggio C++ per necessità. 
 +Ovviamente non esistono oggetti //void//. Il tipo //void// viene usato solo per indicare "nessun valore restituito" da una funzione.
 +
 +Il puntatore //void*// è usato solo per poter puntare qualsiasi cosa, che il programma non sa cosa sia (mentre il programmatore deve sapere cosa e').
 +Il tipo //void*// serve solo per copiare un indirizzo di memoria a basso livello e non consente di lavorare sul contenuto della memoria.
 +
 +Questo significa che per poter lavorare sul contenuto bisogna effettuare un casting verso un altro tipo di puntatore. Mentre il casting tra altri tipi può essere implicito, per i puntatori è necessario usare il casting esplicito... 
 +Mentre esistono conversioni implicite tra float e int, non esiste tra float* e int*
 +