•L’ operatore unario di indirizzo & - Università degli Studi di Roma … · • In C++ è...
Transcript of •L’ operatore unario di indirizzo & - Università degli Studi di Roma … · • In C++ è...
Laboratorio di Informatica – Antonio Monteleone 60
Operatore di indirizzo• L’ operatore unario di indirizzo &
restituisce l’indirizzo della locazione di memoria dell’operando
• Il valore restituito non va usato come l-value (in quanto l’indirizzo di memoria di una variabile non può essere assegnato in un’istruzione, ma è predeterminato)
&a; // ok&(a+1); // errore&a = b; // errore
l’indirizzo di memoria di una variabile non può essere modificato in un’istruzione, ma solo usato come riferimento in quanto è predeterminato e non modificabile)
Laboratorio di Informatica – Antonio Monteleone 61
Puntatori• Una variabile di tipo puntatore contiene
l’indirizzo di memoria di un’altra variabile (detta variabile puntata) la quale contiene un valore specifico
j
12
ptr
int main()
{
int j = 12;
int *ptr = &j;
cout << *ptr << endl;
j = 24;
cout << *ptr << endl;
cout << ptr << endl;
return 0;
}
12
24
0x7b03a928
Laboratorio di Informatica – Antonio Monteleone 62
Dichiarazione di puntatori• Nella dichiarazione si usa *
int *ptr; // ptr è un puntatore a variabile di tipo int• Un dato puntatore può puntare solo al tipo di variabili
specificato nella dichiarazionefloat a = 10; int b = 12;int *ptr1;ptr1 = &a; // errore sintatticoptr1 = &b; // Ok
• Si può anche dichiarare un puntatore a puntatoredouble **ptr_ptr;
• La dichiarazione di un puntatore comporta allocazione di memoria per la variabile puntatore, non per la variabile puntata
Laboratorio di Informatica – Antonio Monteleone 63
Operazioni sui puntatori• Assegnazione di un valore int i = 10;int *p = &i;int *q = p;
• Uso del puntatore per riferirsi all’oggetto puntato*p = 3; // dereferenziazione
• Confronto tra puntatori (==, !=)if (p != q)*p = 3;
Laboratorio di Informatica – Antonio Monteleone 64
Aritmetica dei puntatori• Il valore assunto da un puntatore è un numero intero che
rappresenta, in byte , un indirizzo di memoria• le operazioni di somma fra un puntatore e un valore intero
(con risultato puntatore), oppure di sottrazione fra due puntatori (con risultato intero) vengono eseguite tenendo conto del tipo della variabile puntata
• Es.: se si incrementa un puntatore a float di 3 unità, il suo valore viene incrementato di 12 byte.
• Se l’espressione p rappresenta l’indirizzo di un oggetto di tipo T, allora l’espressione p + 1 rappresenta l’indirizzo di un oggetto di tipo T allocato consecutivamente in memoria.
• Se i è un intero e p rappresenta l’indirizzo addr di T che occupa n locazioni di memoria, allora l’espressione p+i ha valore addr+n*i
Laboratorio di Informatica – Antonio Monteleone 65
L’operatore di dereferenziazione• L’operatore unario di derefenziazione di un
puntatore restituisce il valore della variabile puntata dall’operando
1. Come r-value esegue un’operazione di estrazionea = *p;
// assegna ad a il valore della variabile puntata da p2. Come l-value, esegue un’operazione di
inserimento*p = a;
// assegna il valore di a alla variabile puntata da p
Laboratorio di Informatica – Antonio Monteleone 66
L’operatore di dereferenziazione (2)• L’operazione di dereferenziazione è inversa a
quella di indirizzo: se assegniamo a un puntatore p l’indirizzo di una variabile a allora la derefenziazione *p di p coincide col valore della variabile a#include <cassert>int main()
{double a = 10;double *p = &a; // assert(*p == a);return 0;
}
Laboratorio di Informatica – Antonio Monteleone 67
L’operatore di dereferenziazione (3)
• Puntatore nullo
#include <iostream>
int main(){int j = 12;int *ptr = 0;cout << *ptr << endl; // crash !
double * dPtr = 0;
if (j >0)
{
double a = 10;
dPtr = &a;
} // a va out of scope
cout << *dPtr << endl // crash !
return 0;}
Segmentation violation(core dumped)
j
12
ptr
• Dangling pointer
a
10
dPtr
Laboratorio di Informatica – Antonio Monteleone 68
I puntatori e il qualificatore const
• Mediante la parola chiave const1. E’ possibile specificare che un dato puntatore
non deve essere usato per modificare l’oggetto puntatoint x = 10, y = 20;const int *ptr1 = &x;*ptr1 = 20; // errore di compilazione
2. E’ possibile specificare che un puntatore non deve essere modificatoint * const ptr2 = &x;ptr2 = &y; // errore di compilazione
Laboratorio di Informatica – Antonio Monteleone 69
Puntatori e array
• In C++ gli array sono trattati come puntatori
int main(){float x[5];int j;for (j = 0; j < 5; j++)
x[j] = 0;
float *ptr = x;*ptr = 1.5; // x[0] = 1.5*(ptr+1) = 2.5; // x[1] = 2.5*(ptr+3) = 3.5; // x[3] = 3.5
}
X
X[0]1.5
X[1] X[2] X[3] X[4]2.5 0.0 3.5 0.0
X+1 X+3
Laboratorio di Informatica – Antonio Monteleone 70
Aree di memoria• In C++ è possibile distinguere due aree distinte di
memoria: memoria stack e memoria heap
• E’ possibile allocare aree di memoria dinamicamente durante l’esecuzione del programma ed accedere ad esse mediante puntatori.
• Gli oggetti così ottenuti sono detti dinamici ed allocati nella memoria libera.
Laboratorio di Informatica – Antonio Monteleone 71
Memoria stack• L’area di memoria stack é quella in cui viene
allocato un “pacchetto” di dati non appena l’esecuzione passa dal programma chiamante a una funzione .Questo “pacchetto” contiene:- l’indirizzo di rientro nel programma chiamante- la lista degli argomenti passati alla funzione
• Esso viene “impilato” sopra il pacchetto precedente (quello del progr. chiamante) e poi automaticamente rimosso dalla memoria appena l’esecuzione della funzione é terminata.
Laboratorio di Informatica – Antonio Monteleone 72
Memoria stack (2)• Nella memoria stack vengono sistemati anche i dati relativi
a tutte le variabili automatiche (cioè locali e non statiche) create dalle funzioni.
• Il loro “ tempo di vita” é legato all’esecuzione della funzione proprio perché, quando la funzione termina, l’intera area stack allocata viene rimossa.int sum(int a[], int len) {int temp=0;for (int i=0; i<len; i++)temp += a[i];
return temp;}
int main() {int vect[4] = {1,2,3,4};int tot = sum(vect, 4);cout << “somma “ << tot << endl;return 0;
}
Laboratorio di Informatica – Antonio Monteleone 73
Memoria heap• L’area heap non é allocata automaticamente, ma
solo su esplicita richiesta del programma ( allocazione dinamica della memoria)
• L’area allocata non é identificata da un nome, ma è accessibile solo tramite dereferenziazione di un puntatore
• Il tempo di vita coincide con l’intera durata del programma, a meno che non venga esplicitamente deallocata; se il puntatore va out of scope, l’area non è più accessibile, ma continua a occupare memoria inutilmente (memory leak).
Laboratorio di Informatica – Antonio Monteleone 74
new e delete• Gli operatori new and delete vengono utilizzati per
allocazione/deallocazione di memoria dinamica– la memoria dinamica (heap), è un’area di memoria libera
provvista dal sistema per quegli oggetti la cui durata di vita è sotto il controllo del programmatore
• new riserva la quantità necessaria di memoria richiesta e ritorna l’indirizzo di quest’area
int *i=new int; //alloca un intero, ritorna il puntatorechar *c=new char[100]; //alloca un array di 100 caratteriint *i=new int(99); //alloca un intero e lo inizializza a 99char *c=new char(‘c’); //alloca un carattere inizializzato a cInt *j=new int[n][4]; //alloca un array di puntatori ad intero
Laboratorio di Informatica – Antonio Monteleone 75
new e delete (2)• L’operatore delete è usato per restituire una certa
area di memoria (allocata con new) allo heap• Ogni oggetto allocato con new deve essere distrutto con delete se non viene piu` utilizzato, altrimenti l’areadi memoria che esso occupata non potra` piu` essere ri-allocata (memory leak)
• L’argomento di delete è tipicamente un puntatoreinizializzato preventivamente con new
delete ptr; // distrugge un puntatore ad un oggettodelete p[i]; // distrugge l’oggetto p[i]delete [] p; // distrugge ogni oggetto di tipo p
Laboratorio di Informatica – Antonio Monteleone 76
new e delete (3)
• Attenzione– la dimensione dello heap non e` infinita– l’allocazione con new può fallire, nel qual caso new
restituisce un puntatore nullo o suscita un’eccezione. Nel caso di allocazione di memoria importantebisogna verificare che l’operazione abbia avutosuccesso prima di usare il puntatore
– ogni oggetto creato con new deve essere distruttocon delete, ogni oggetto creato con new[] deveessere distrutto con delete[] , queste forme NONsono intercambiabili
Laboratorio di Informatica – Antonio Monteleone 77
Puntatori: allocazione dinamica• Riferimento ad una locazione di memoria
#include <iostream>
int main(){int *ptr = new int;
*ptr = 12;cout << *ptr << endl;
delete ptr;return 0;
}
12
ptr
• Attenzione:– Non usare delete fa accumulare locazioni di
memoria inutilizzate (memory leak)– Utilizzare puntatori prima del new o dopo il delete causa il crash del programma
Laboratorio di Informatica – Antonio Monteleone 78
Puntatori: allocazione dinamica
• Riferimento a più locazioni di memoria#include <iostream>
int main(){int *ptr = new int[3];
ptr[0] = 10;ptr[1] = 11;ptr[2] = 12
delete [] ptr;return 0;
}
10
ptr
11 12
Laboratorio di Informatica – Antonio Monteleone 79
Regole di conversione e cast• In C++ esistono conversioni esplicite ed implicite.
Le conversioni implicite (e.g. int →float) nelleespressioni aritmetiche, nel passare i parametri ad unafunzione o nel ritornare un valore da una funzionerendono il meccanismo di conversione molto conveniente ma anche potenzialmente pericoloso(errori a run time)
•char, short e bool vengono promossi ad int•Tipi interi che non possono essere rappresentati con un int
vengono promossi a unsigned•In una espressione di tipo misto, gli operandi di ordine
inferiore vengono promossi all’ordine superiore secondola gerarchia:
int<unsigned<long<unsigned long<float<double<long double•bool e` un tipo intero, con true che viene promosso a 1 e false
a 0
Laboratorio di Informatica – Antonio Monteleone 80
Regole di conversione e cast (2)• Ogni genere di puntatore può essere convertito in
un puntatore generico a void• Al contrario di quanto avviene in C, un puntatore
generico non è compatibile con un puntatore ditipo arbitrario ma richiede un cast esplicito
• Ogni puntatore puo` essere inizializzato a 0 senzabisogno di un cast esplicito.
• In C++ usare 0 e non NULL per i puntatori!
char *ch;void *generic_p;. . .generic_p=ch; // OK, char* va in void*ch=generic_p; // OK in C, illegale in C++ch=(char *)generic_p; // OK, C e C++ arcaico
Laboratorio di Informatica – Antonio Monteleone 81
Casting in ANSI C++• Data la complessità delle operazioni di casting in C++ nuovi
operatori di casting sono stati aggiunti a quelli già esistentiin C
• Esiste anche un dynamic_cast, utilizzato per riconoscere il tipo di un oggetto a run-time (RTTI)
x=(float) i; // cast in C++ - notazione Cx=float(i); // cast in C++, notazione funzionale
x=static_cast<float>(i); // ANSI C++ - raccomandatoi=reinterpret_cast<int>(&x) // ANSI C++, non portabile e
// system dependent
func(const_cast<int>(c_var)) // dove C_var e` una// variabile dichiarata// const. Usato per // eliminare la “const-ness” // per chiamare func
Laboratorio di Informatica – Antonio Monteleone 82
L’operatore static_cast• L’operatore static_cast<tipo>(espressione)
è usato per convertire un tipo in un altro, compatibile con il primo. int a = static_cast<int>(12.45); // da double a int
• L’espressione a += b; // a di tipo int, b di tipo double
è valutata dal compilatore come se fossea=static_cast<int>(static_cast<double>(a)+b);
Si ha lo stesso risultato esplicitando il casta += static_cast<int>(b);
Laboratorio di Informatica – Antonio Monteleone 83
L’operatore const_cast• L’operatoreconst_cast<tipo>(espressione)
è usato per eludere l’attributo const del suo operando
void fun(char *s); // non altera la stringa
int main() {char const hello[] = “ciao“;
fun(hello) // errore// cannot convert parameter 1 from 'const char [5]' to 'char *‘
fun(const_cast<char *>(hello)); // OK
return 0;}
Elimina la const-ness
Laboratorio di Informatica – Antonio Monteleone 84
L’operatore reinterpret_cast• L’operatorereinterpret_cast<tipo>(espressione)è usato per reinterpretare i bit che compongono il suo argomento
void swap(char *c1, char *c2); // scambia i due argomentivoid swap(char &c1, char &c2); // parametri per riferimento
void swapLong(long *a) // scambia i byte dell’argomento{char *c = reinterpret_cast<char*> (a);swap(c, c+3); // swap(c[0], c[3]); swap(c+1, c+2); // swap(c[1], c[2]);
}