Università dell’Insubria
Facoltà di Scienze Matematiche, Fisiche e Naturali di Varese
Corso di Laurea in Informatica
Anno Accademico 2007/08
Laboratorio di Linguaggi
lezione VI: puntatori
Marco Tarini
2/3
Assegnare i Puntatori
• In memoria, un puntatore è un indirizzo di memoria
– (...di una variabile)
– (...di cui e' noto il tipo)
• Bene, ma quale indirizzo?
– Modo 1: prendere l'indirizzo di una variabile esistente
• il puntatore punterà a quella variabile
– Modo 2: allocare (riservare, prenotare) della memoria libera
• il puntatore punterà ad una nuova variabile, memorizzata nella
memoria così riservata
• la nuova variabile è allocata dinamicamente!
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Assegnare i Puntatori
• Modo 1: prendere l'indirizzo di una variabile esistente
– il puntatore punterà a quella variabile
• Operatore "ampersand" (
• Esempio:
il puntatore p
punta
all'indirizzo di
memoria
dove vive la
variabile d
&
)
double d = 9.0;
double *p;
p = &d;
printf("%f",*p);
*p = 21.5;
printf("%f",d);
scrivi il valore
di *p .
Cosa scrive?
scrivi il valore
di d .
Cosa scrive?
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Operatore & e i tipi
• se y è una var di tipo T...
& y
T
T*
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Operatore
errore di tipo!
numeri non
è mica di tipo
int !
(e quindi
&numeri non
è di tipo
int* )
& e vettori
int numeri[]={10,20,30,40};
int *punt;
punt = & numeri;
si può scrivere invece:
punt = & (numeri[0]);
oppure anche:
punt = numeri;
scriviamo tutti e 4 i numeri:
int i;
for (i=0; i<4; i++) {
printf("%d ", numeri[i]);
}
Marco Tarini - Laboratorio di Linguaggi -
usando i puntatori:
int i;
for (i=0; i<4; i++) {
printf("%d ", *(punt++));
}
2007/08 - Università dell’Insubri
Altro Esempio
stringa
stringa[0]
'p'
p
stringa[1]
'u'
p
stringa[2]
'n'
p
stringa[3]
't'
p
stringa[4]
'a'
p
stringa[5]
't'
p
stringa[6]
'o'
p
stringa[7]
'r'
p
stringa[8]
'e'
p
stringa[9]
0
p
char stringa[]="puntatore";
int i;
while (stringa[i]) {
stringa[i] = maiuscolo(stringa[i]);
i++;
}
char stringa[]="Puntatore";
char *p = stringa;
while (*p) {
*p = maiuscolo( *p );
p++;
}
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Uso dei Puntatori come Parametri
• vi ricordate quel problemino?
void raddoppia (int
{
x = x * 2;
}
x)
int main(){
int incassi = 5;
raddoppia( incassi );
...
}
void raddoppia (int *x)
{
*x = *x * 2;
}
int main(){
int incassi = 5;
raddoppia( & incassi );
...
}
Remember: in C i paramatri sono passati per copia !
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Uso dei Puntatori come Parametri
• un'altra motivazione possibile: efficienza
typedef struct {
char nome[20];
char cognome[20];
int eta;
Esami* esami_sostenuti[50];
} Persona;
int eta_fra_10_anni (Persona pp)
{
return pp.eta + 10;
}
int eta_fra_10_anni (Persona * pp)
{
return pp->eta + 10;
}
Marco Tarini - Laboratorio di Linguaggi -
ogni volta che si chiama
questa funzione,
vengono copiati
sizeof(Persona)
bytes.
ogni volta che si chiama
questa funzione,
vengono copiati
sizeof(Persona*)
bytes.
2007/08 - Università dell’Insubri
Uso dei Puntatori come Parametri
• un'altra motivazione possibile: efficienza
int eta_fra_10_anni (Persona pp)
{
return pp.eta + 10;
}
inefficiente 
int eta_fra_10_anni (Persona * pp)
{
return pp->eta + 10;
}
efficiente 
In questi casi, però, meglio aggiungere anche la keyword
const
:
int eta_fra_10_anni (const Persona * pp)
{
return pp->eta + 10;
}
• più informazione presente nel codice per il programmatore
(un po' come un commento)
• più ottimizzazioni possibili da parte del compilatore
• più controllo di errori a tempo di compilazione
(per esempio se per sbaglio si tenta di cambiare il valore del parametro)
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Uso dei Puntatori come Parametri
• Riassumendo:
void Procedura( TipoParametro x)
{
...
}
passaggio di parametro
per copia
(l'unico possibile in C)
void Procedura( TipoParametro* x)
{
...
}
void Procedura( const TipoParametro* x)
{
...
}
Marco Tarini - Laboratorio di Linguaggi -
tecnicamente, altri
passaggi di parametro
per copia
ma ciò che si copia è un
puntatore, cioè un
riferimento!
in pratica, (simulazione di)
un passaggio di parametro
per riferimento
2007/08 - Università dell’Insubri
Uso dei Puntatori come Parametri
• Riassumendo:
void Procedura( TipoParametro x)
{
...
}
usare quando la procedura
non deve cambiare il valore del
parametro
void Procedura( TipoParametro* x)
{
...
}
usare quando la procedura
deve cambiare il valore del
parametro
void Procedura( const TipoParametro* x)
{
...
}
Marco Tarini - Laboratorio di Linguaggi -
usare quando la procedura
non deve cambiare il valore del
parametro
...ma sarebbe troppo oneroso fare
la copia del parametro
(parametro voluminoso!)
2007/08 - Università dell’Insubri
Assegnare i Puntatori
• In memoria, un puntatore è un indirizzo di memoria
– (...di una variabile)
– (...di cui e' noto il tipo)
• Bene, ma quale indirizzo?
– Modo 1: prendere l'indirizzo di una variabile esistente
• il puntatore punterà a quella variabile
– Modo 2: allocare (riservare, prenotare) della memoria libera
• il puntatore punterà ad una nuova variabile, memorizzata nella
memoria così riservata
• la nuova variabile è allocata dinamicamente!
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Allocazione
void* malloc(unsigned int n);
funzione malloc ( sta per memory allocation )
1 - alloca n bytes di memoria.
2 - restituisce l' indirizzo della memoria appena allocata
• sotto forma di puntatore generico !
• "
" puntatore generico,
puntatore
void* senza tipo,
in pratica, un semplice indirizzo di memoria
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Allocazione: esempio
int* p;
p = malloc( ?
4 );
Ma il tipo non torna!
A sx abbiamo un (int*) mentre a dx un (void*)
Avviene un Typecast fra puntatori
Possiamo anche renderlo esplicito (meglio):
int* p;
p = (int*) malloc(4);
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Typecast fra puntatori
• Ogni tipo puntatore puo essere trasformato in un
qualsiasi altro tipo di puntatore: (implicitamente o esplicitamente)
– da int* a double* ,
da void* a Persona*
etc.
• Semantica:
= “quello che fa”
– L’indirizzo rimane lo stesso,
cambia l’interpretazione di ciò che ci trovo
– Nota: in effetti, a tempo di esecuzione, non avviene nulla!
Cambiano solo cose nella testa del compilatore
• Costrutto estremamente POTENTE
e di basso livello
(e potenzialmente molto pacciugone)
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Typecast fra puntatori: primo esempio
Implicito:
int* p;
p = malloc( 4 );
Esplicito (meglio, più chiaro):
int* p;
p = (int*) malloc(4);
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Allocazione: e se la memoria finisce?
void* malloc(unsigned int n);
Se non c'è più memoria, l'allocazione "fallisce"
e malloc restituisce il valore speciale NULL
• semanticamente, NULL è un "puntatore che non punta a nulla"
• NULL è rappresentato dal valore 0
Quindi, il valore resituito dalle malloc va controllato !
int* p;
p = (int*) malloc(4);
if (p == NULL) {
/* finita memoria ... */
}
Marco Tarini - Laboratorio di Linguaggi -
oppure,
più coincisamente
if (!p) {
2007/08 - Università dell’Insubri
Allocazione
typedef struct {
/*blah blah... un sacco di campi, array...*/
} TipoStrano
TipoStrano* p;
p = (TipoStrano *) malloc(sizeof(TipoStrano));
Il costrutto sizeof è estremamente utile
con le malloc.
Usare sempre, anche con i tipi base
• int, short, float, double...
• remember: il C non prescrive quanti bytes occupano!
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Dellocazione
void free(void* p);
libera la memoria che era stata allocata all'indirizzo p.
Nota: p deve essere il risultato di una malloc!
int* p;
p = (int*) malloc(sizeof(int));
... /* Qui uso (*p) */
free(p);
se mi dimentico di deallocare, ho un cosiddetto memory leak
Remember: non c'è alcuna garbage collection in C !
Marco Tarini - Laboratorio di Linguaggi -
2007/08 - Università dell’Insubri
Allocazione e Deallocazione: esempio
usando l'allocazione automatica delle
variabili locali:
k viene automaticamente allocato (i 4 bytes di
memoria necessari al suo immagazzinamento
vengono "prenotati").
int proc() {
int k;
k=15;
k viene inizializzato (a 15)
... /* lavora con k */
return 0;
all'uscita dalla procedura, i 4 bytes sono
resi di nuovo disponibili
};
k viene esplicitamente allocato. (a tempo di
esecuzione, si trovano i 4 bytes di memoria necessari
al suo immagazzinamento. La locazione viene
memorizzata in k).
k viene inizializzato (a 15)
all'uscita dalla procedura, dobbiamo
rendere i 4 bytes di nuovo disponibili
esplicitamente
Marco Tarini - Laboratorio di Linguaggi -
usando l'allocazione dinamica:
int proc() {
int* k;
k = (int*)malloc(
sizeof(int)
);
*k = 15;
... /* lavora con *k */
free(k);
return 0;
};
2007/08 - Università dell’Insubri
Scarica

ppt