Università dell’Insubria
Facoltà di Scienze Matematiche, Fisiche e Naturali di Varese
Corso di Laurea in Informatica
Anno Accademico 2004/05
Laboratorio di Linguaggi
lezione IV
Marco Tarini
Laboratorio di Linguaggi
• docente: Marco Tarini
e-mail: [email protected]
• ricevimento: Martedì 14:30 - 17:30
o anche su appuntamento
• libro di testo consigliato:
Kelley Al, Pohl Ira:
"C Didattica e Programmazione" ("A Book on C")
quarta edizione - anche la terza va bene
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
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 ‧
2004/05 ‧ Università dell’Insubria
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;
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Uso dei Puntatori come Parametri
• vi ricordate quel problemino?
void raddoppia (int
{
x = x * 2;
}
x)
void raddoppia (int* x)
{
*x = *x * 2;
}
int main(){
int incassi = 5;
raddoppia( incassi );
...
}
int main(){
int incassi = 5;
raddoppia( &incassi );
...
}
compila, ma non funziona 
non raddoppio "incassi", ma una sua
copia temporanea
funziona 
Remember: in C i paramatri sono passati sempre per copia !
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
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
(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 ‧
2004/05 ‧ Università dell’Insubria
Uso dei Puntatori come Parametri
• Riassumendo:
void Procedura( TipoParametro x)
{
...
}
void Procedura( TipoParametro* x)
{
...
}
void Procedura( const TipoParametro* x)
{
...
}
Marco Tarini ‧ Laboratorio di Linguaggi ‧
passaggio di parametro per copia
(l'unico possibile in C)
tecnicamente, un altro
passaggio di parametro per copia
ma ciò che si copia è un
puntatore, cioè un riferimento!
in pratica, (simulazione di) un
passaggio per riferimento
2004/05 ‧ Università dell’Insubria
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)
{
...
}
(chiaramente, lo stesso vale per le funzioni)
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!)
2004/05 ‧ Università dell’Insubria
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 ‧
2004/05 ‧ Università dell’Insubria
Allocazione
void* malloc(unsigned int n);
funzione malloc = m-emory alloc-ation
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 ‧
2004/05 ‧ Università dell’Insubria
Allocazione: esempio
int* p;
p = malloc( ?
4 );
Errore di tipo!
A sx abbiamo un (int*) mentre a dx un (void*)
Il tipo è diverso, ma si tratta sempre di un indirizzo
di memoria!
Soluzione: basta fare un type-cast:
int* p;
p = (int*) malloc(4);
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
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"
• in memoria, NULL è rappresentato dal valore
0
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) {
2004/05 ‧ Università dell’Insubria
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 ‧
2004/05 ‧ Università dell’Insubria
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));
... /* 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 ‧
2004/05 ‧ Università dell’Insubria
Allocazione e Deallocazione: esempio
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;
};
2004/05 ‧ Università dell’Insubria
Vi ricordate quell'altro problemino...
• Cosa succede, se non si sa a priori* quanti
elementi di un array ci serviranno**?
– * quando scriviamo il programma
– ** a tempo di esecuzione
• Necessità allocazione dinamica di array.
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Allocazione di vettori
(void*) calloc(unsigned int n, unsigned int size);
calloc = c-ontiguous alloc-ation
Alloca n elementi contigui ciascuno grande size.
In pratica, alloca un area di memoria grande n x size
Per il resto funziona come malloc
Esempio:
int* p;
p = (int*) calloc(100000,sizeof(int) );
Alloca un vettore di 100000 interi.
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Allocazione di vettori
• Ricordiamoci sempre:
B) vettore allocato dinamicamente:
int* v = (int*) calloc(100000,sizeof(int) );
A) fixed size vector:
int v[100000];
• In entrambi i casi:
• ho un vettore di 100000 interi
• posso scrivere ad esempio:
v[2]= v[0] + 3 * v[1] ;
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Allocazione di vettori
• Ricordiamoci sempre:
B) vettore allocato dinamicamente:
int* v = (int*) calloc(100000,sizeof(int) );
A) fixed size vector:
int v[100000];
• Differenza 1: dimensione variabile
• se x è una var intera, posso scrivere:
int* v = (int*) calloc(x,sizeof(int) );
• ma non posso scrivere:
int v[x];
qua è richiesta una costante!
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Allocazione di vettori
• Ricordiamoci sempre:
B) vettore allocato dinamicamente:
int* v = (int*) calloc(100000,sizeof(int) );
A) fixed size vector:
int v[100000];
• Differenza 2: se ho allocato, devo deallocare
int* v = (int*) calloc(100000,sizeof(int) );
... /* usa v */
free(v);
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Allocazione di vettori
• Ricordiamoci sempre:
B) vettore allocato dinamicamente:
int* v = (int*) calloc(100000,sizeof(int) );
A) fixed size vector:
int v[100000];
• Differenza 3: fixed size = più efficiente
•
•
•
•
•
il solito prezzo da pagare per l'uso dei puntatori...
0xAA000000 + 2 x sizeof(int)
...maggiorato
ma precalcolato staticamente
mettiamo che v valga 0xAA000000 :
v[2]
se fixed:
READ TEMP 0xAA000008
compilazione
se dinamco:
READ TEMP0 0xAA000000
v[2]
compilazione
Marco Tarini ‧ Laboratorio di Linguaggi ‧
ADD TEMP0
READ TEMP
8
TEMP0
2004/05 ‧ Università dell’Insubria
Allocazione di vettori
• Ricordiamoci sempre:
B) vettore allocato dinamicamente:
int* v = (int*) calloc(100000,sizeof(int) );
A) fixed size vector:
int v[100000];
• Differenza 4:
• vengono allocati in zone diverse della memoria...
• come vedremo nella lezione sulla
gestione della memoria
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Puntatori: operazioni a basso livello
Esercizio:
un numero in
virgola mobile
a doppia precisione
di solito, ma dipende
dall'implementazione
• Sappiamo che un double occupa 8 bytes.
• Dato un double, quale è il valore di questi 8
bytes?
• Per esempio, quali 8 bytes compongono il
valore 3.14159256 ?
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Puntatori: operazioni a basso livello
1B903C32
1B903C33
1B903C34
1B903C35
1B903C36
1B903C37
1B903C38
1B903C39
1B903C3A
1B903C3B
1B903C3C
spazio riservato per la variabile d (8 bytes)
int main()
{
dobule d;
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Puntatori: operazioni a basso livello
1B903C32
1B903C33
1B903C34
1B903C35
1B903C36
1B903C37
1B903C38
1B903C39
1B903C3A
1B903C3B
1B903C3C
codifica di 3.14159256
spazio riservato per la variabile d (8 bytes)
un puntatore a
Byte che farà
da vettore di
Bytes
ma il tipo "Byte"
non è definito!
Definirlo, ad es
fuori dalla proc
"main" (vedere
lez. 2)
int main()
{
dobule d;
d = 3.14159256;
Byte * v ;
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Puntatori: operazioni a basso livello
1B903C32
1B903C33
1B903C34
1B903C35
1B903C36
1B903C37
1B903C38
1B903C39
1B903C3A
1B903C3B
1B903C3C
vc[0]o dv[1]
v[6]2 5v6
[7]
i f i vc[2]a dv[3]
i 3v.[4]1 4v[5]
159
spazio riservato per la variabile d (8 bytes)
int main()
{
dobule d; int i;
d = 3.14159256;
Byte * v ;
v = &
(Byte*)
d;
& d;
for (i=0; i<
i<8;
sizeof(double);
i++)
i++)
printf("%d,",v[i]);
system("Pause");
};
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Puntatori: operazioni a basso livello
• A casa, provate questo programma, e scopriamo:
– quali sono gli 8 bytes che compongono il double 3.14159256
– quali sono gli 8 bytes che compongono il double 6.022 x 1023
• e, adattando il programma agli interi:
– quali sono i 4 bytes che compongono l'int +1000000
– quali sono i 4 bytes che compongono l'int +1
– quali sono i 4 bytes che compongono l'int -1
• a seconda di quale architettura viene usata, potremmo
trovare risposte diverse!
– domanda: sarebbe potuto succedere in Java?
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Puntatori
• Molto potenti...
– vettori di dimensione determinata dinamicamente
– passaggio di parametri per riferimento
• in un linguaggio che prevede passaggio solo per copia
– possibilità di scrivere codici più efficienti
– controllo diretto delle risorse, a basso livello
– strutture dati flessibili
• per esempio:
typedef struct {
char nome[20];
char cognome[20];
int eta;
Persona* padre, madre;
} Persona;
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Puntatori
• Molto potenti...
"Ad un grande potere corrisponde una
grande responsabilità."
– lo zio di Spiderman
Marco Tarini ‧ Laboratorio di Linguaggi ‧
2004/05 ‧ Università dell’Insubria
Scarica

lucidi