Sei sulla pagina 1di 22

3817$725,H$//2&$=,21(

',1$0,&$

9Riflessione sul passaggio per riferimento

9Puntatori
9Puntatori e Strutture

9Allocazione dinamica della memoria


9Allocazione e Strutture

3XQWDWRULH$OORFD]LRQH'LQDPLFD 1
3$66$**,23(55,)(5,0(172
5,)/(66,21,
Il passaggio per riferimento consente di superare
i limiti della semantica per copia.
Quando un parametro è passato per riferimento,
la procedura (o funzione) riceve QRQXQDFRSLD
GHOYDORUHdel parametro attuale, ma XQ
ULIHULPHQWRDHVVR.
Nella corrispondente cella del record di
attivazione viene perciò inserito O¶LQGLUL]]Rdella
variabile che costituisce il parametro attuale, non
il suo valore.
ESEMPIO
'/ 5$ void scambia2(int& A, int& B)
;   D {
<   E
int T;
'/ 5$
$ D if (A>B)
{ T=A; A=B; B=T; }
% E
}
7 

3XQWDWRULH$OORFD]LRQH'LQDPLFD 2
3$66$**,23(55,)(5,0(172
,03/,&$=,21,
Per gestire il passaggio per riferimento, la
macchina virtuale del linguaggio:
• inserisce nel record di attivazione delle
funzione LQGLUL]]LGLYDULDELOLanziché valori
• GHUHIHUHQ]LD il riferimento ogni volta che esso
viene usato, raggiungendo, a partire da esso,
la variabile originale del chiamante.
Il dereferenziamento viene realizzato a livello
macchina mediante un indirizzamento indiretto.
Nei linguaggi in cui è direttamente disponibile
(come il C++), il passaggio per riferimento è
gestito interamente dal linguaggio.
Se il linguaggio (come il C) non fornisce
direttamente il passaggio per riferimento,
bisognerà costruirlo.

3DVVDJJLRSHUULIHULPHQWRLQ&

*HVWLUHLOSDVVDJJLRSHUULIHULPHQWRLPSOLFD
ODFDSDFLWjGLDFFHGHUHGLUHWWDPHQWHDJOL
LQGLUL]]LGHOOHYDULDELOL

3XQWDWRULH$OORFD]LRQH'LQDPLFD 3
$&&(662$//$0$&&+,1$
6277267$17(
Il C prevede meccanismi per:
• ULFDYDUHO¶LQGLUL]]Rdi una variabile
• operatore HVWUD]LRQHGLLQGLUL]]R:&
• GHUHIHUHQ]LDUHXQLQGLUL]]Rdi variabile, ossia
“recuperare” la variabile dato il suo indirizzo.
• operatore di GHUHIHUHQ]LDPHQWR: *
Se x è una variabile, &x denota l’LQGLUL]]RLQ
PHPRULDFHQWUDOHdi tale variabile.
&x ≡ α
/LYHOORGLDVWUD]GHO
OLQJXDJJLRGLDOWR
OLYHOOR /LYGLDVWUD]GHOOD
PDFFKLQDEDVH

x α

Se α è l’indirizzo in memoria centrale di una


variabile, *α denota tale variabile:
*α ≡ x

3XQWDWRULH$OORFD]LRQH'LQDPLFD 4
3817$725,
Un SXQWDWRUH è il costrutto linguistico introdotto
dal C (e da molti altri linguaggi) come forma di
accesso alla macchina sottostante.

8Q7,32´SXQWDWRUHD7µqXQWLSRFKH
GHQRWDO·LQGLUL]]RGLPHPRULDGLXQDYDULDELOH
GLWLSR7

W\SHGHI <tipo> <tipoPuntatore>


ESEMPIO
typedef int* puntint;
typedef int *puntint;

8Q3817$725( D7 qXQDYDULDELOHGLXQ


WLSRSXQWDWRUH D7 FKHSXzFRQWHQHUH
O·LQGLUL]]RGLXQDYDULDELOH GLWLSR7 

WLSR3XQWDWRUH!<nomeVariabile> ;
WLSR!* <nomeVariabile> ;
ESEMPIO
puntint p ;
int *p ;
int * p ;

3XQWDWRULH$OORFD]LRQH'LQDPLFD 5
3817$725, VHJXH 
Un puntatore è una variabile GHVWLQDWDD
FRQWHQHUHO¶LQGLUL]]RGLXQ¶DOWUDYDULDELOH.
Un puntatore QRQSXzFRQWHQHUHXQLQGLUL]]R
TXDOXQTXHesiste un vincolo di tipo.
La sintassi traduce tale vincolo consentendo di
definire QRQXQ³SXQWDWRUHTXDOXQTXH´, ma un
SXQWDWRUHDXQFHUWRWLSR7
ESEMPIO
int x = 8; int *p;
p = &x; /* OK */

Da questo momento, *p e x sono due modi


diversi di denotare ODVWHVVDYDULDELOH:
p
α β x α
8

Quindi si potrà avere, ad esempio:


int x = 8; int *p = &x;
*p = 51; x--;
Il valore di *p alias x è ora 50.
Il puntatore p resta invece immutato
(vale sempre α).

3XQWDWRULH$OORFD]LRQH'LQDPLFD 6
3817$725, VHJXH 
Riprendiamo l’esempio:
p x int x = 8;
α β 8 α int *p = &x;
Da questo momento, *p e x sono due modi
diversi di denotare ODVWHVVDYDULDELOH:
ATTENZIONE: Non confondere LOYDORUHGHO
SXQWDWRUH(qui, α) con O¶LQGLUL]]RGHOO¶DUHDGL
PHPRULDFKHRVSLWDLOSXQWDWRUH(qui, β).
8QSXQWDWRUHQRQqOHJDWRDOO¶RJJHWWRSXQWDWR
LQYLDGHILQLWLYD: il puntatore può essere fatto
puntare a un’altra variabile assegnandogli un
nuovo indirizzo.
ESEMPIO
int x = 8, y = 66;
int *p = &x;
(*p)++; /* incrementa x */
p = &y; /* ora p punta a y */
(*p)--; /* decrementa y */
127$ OH SDUHQWHVL LQ (*p)++ VRQR QHFHVVDULH SHU
VSHFLILFDUH GL LQFUHPHQWDUH O·RJJHWWR SXQWDWR GD
pQRQpVWHVVR

3XQWDWRULH$OORFD]LRQH'LQDPLFD 7
3817$725,H&203$7,%,/,7$¶',7,32
Un puntatore a T può contenere solo indirizzi di
variabili di tipo T:
WLSLGLSXQWDWRULGLYHUVLVRQRLQFRPSDWLELOLIUD
ORUR
int x = 8; float *q;
q = &x; /* NO! */
oppure:
int x = 8, *p; float *q;
p = &x;
q = p; /* NO! */
027,92 l’informazione sul tipo GHOSXQWDWRUH
serve a dedurre il tipo GHOO¶RJJHWWRSXQWDWR, che è
LQGLVSHQVDELOH per effettuare correttamente il
dereferenziamento.
)RU]DUHXQDVVHJQDPHQWRIUDSXQWDWRUL
LQFRPSDWLELOLULFKLHGHXQFDVWHVSOLFLWR
int x = 8; float *q;
q = (float*)&x; /* NO! */
• È facile scriverlo…
• … ma quasi sempre si ottengono VRORJXDL!
$JJLUDUHLOW\SHV\VWHPQRQqTXDVLPDL
XQDEXRQDLGHD

3XQWDWRULH$OORFD]LRQH'LQDPLFD 8
3817$725,H67587785(
Per le strutture, l’accesso all’indirizzo avviene
nella maniera consueta.
ESEMPIO
typedef struct {
char nome[20], cognome[20];
int eta;
} persona;
persona p;
SHUVRQD SS  S

È quindi possibile accedere a un singolo membro


di un puntatore p a struttura attraverso gli
operatori di dereferenziamento:
• (*p).membro
dove le parentesi sono necessarie perché la
precedenza di . è superiore a quella di *
• p->membro
ESEMPIO
++ p->eta; /* incrementa il valore di età */
p->nome[0] /* accede al primo carattere del
nome */
127$ ++p->eta ++(p->eta) LQ TXDQWR OD
SUHFHGHQ]DGL->qVXSHULRUHDTXHOODGL++

3XQWDWRULH$OORFD]LRQH'LQDPLFD 9
',&+,$5$=,21,67$7,&+(H9(7725,
In C, le variabili fin qui viste devono essere
GLFKLDUDWHVWDWLFDPHQWH, ossia la loro esistenza
e il loro nome devono essere SUHYLVWLDSULRUL
Mentre per le variabili di tipo scalare ciò non
costituisce di norma un problema, può esserlo
per variabili di tipo YHWWRUH.
Infatti, in C ODGLPHQVLRQHGLXQYHWWRUHGHYH
HVVHUHGLFKLDUDWDFRPHFRVWDQWHQRWDDSULRUL:
int v[4];
char nome[20];
Ciò rappresenta un limite in quei casi in cui q
LPSRVVLELOHVDSHUHDSULRULODTXDQWLWjGLGDWLLQ
LQJUHVVR.
Sarebbe molto utile poter dimensionare un
vettore GLQDPLFDPHQWH, ossia sulla base del
valore di una variabile (a tempo d’esecuzione).
PIU’ IN GENERALE:
per superare la rigidità della dichiarazione
statica, occorre un modo per
“chiedere al sistema nuova memoria” DOELVRJQR.
4XHVWRqSRVVLELOHJUD]LHDOFRQFHWWR
GLDOORFD]LRQHGLQDPLFD

3XQWDWRULH$OORFD]LRQH'LQDPLFD 10
$//2&$=,21(',1$0,&$'(//$
0(025,$
Il C consente (come molti altri linguaggi) di
“chiedere nuova memoria” DOPRPHQWRGHO
ELVRJQR, tramite una SULPLWLYDGLVLVWHPD:
ODIXQ]LRQHPDOORF GHOODOLEUHULDVWGOLEK
La funzione malloc():
• chiede al sistema di DOORFDUHXQ¶DUHDGL
PHPRULDGHOODGLPHQVLRQHVSHFLILFDWD
• restituisce l’LQGLUL]]RGHOO¶DUHDGLPHPRULD
DOORFDWD
Se l’allocazione non è stata possibile, malloc()
restituisce un SXQWDWRUH18//  ,FKHVHJQDOD
O¶HUURUH
Nel codice del programma C occorre quindi:
• specificare la dimensione dell’area desiderata
(LQE\WH)
• mettere LQXQSXQWDWRUH l’indirizzo restituito da
malloc()
Inoltre, poiché malloc() restituisce XQSXUR
LQGLUL]]R, senza informazioni di tipo, q
LQGLVSHQVDELOHXQFDVWper “etichettare” l’area di
memoria come contenente dati di un certo tipo.

3XQWDWRULH$OORFD]LRQH'LQDPLFD 11
$//2&$=,21(',1$0,&$'(//$
0(025,$ VHJXH 
ESEMPIO
int *p;
p = (int*) malloc (12);

Da questo momento, l’area così allocata può


essere usata WUDPLWHLOSXQWDWRUHFKHQHFRQWLHQH
O¶LQGLUL]]RLQL]LDOH
L’esempio precedente è formalmente corretto,
ma il numero di interi allocati è GLSHQGHQWHGDOOR
VSHFLILFRFRPSLODWRUH perché la dimensione dei
vari tipi non è standard.
'LP 12 byte 'LP,QW 2 byte 1ƒLQW 6
'LP 12 byte 'LP,QW 4 byte 1ƒLQW 3
È opportuno QRQLQVHULUHPDLYDORULQXPHULFL
GLUHWWDPHQWH, ma utilizzare invece l’apposito
RSHUDWRUHVL]HRI 
Ad esempio, per allocare spazio per cinque interi:
int *p;
p = (int*) malloc (5*sizeof(int));

1ƒLQW 5 VL]HRI LQW 2 byte 'LP 10 byte


1ƒLQW 5 VL]HRI LQW 4 byte 'LP 20 byte

3XQWDWRULH$OORFD]LRQH'LQDPLFD 12
$//2&$=,21(',1$0,&$'(//$
0(025,$ VHJXH 
Dato p di tipo <type>* attraverso l’struzione
p = (<type>*) malloc(N*sizeof(<type>));
S

VLRWWLHQHXQYHWWRUHGLQDPLFRGL1HOHPHQWL
L’area di memoria allocata è usabile tramite il
puntatore. Le due notazioni equivalenti sono:
• o tramite la notazione *p
• o tramite la notazione p[i]
Nel caso in cui N=1, si alloca spazio SHUXQD
VLQJRODYDULDELOH
S
int *p = (int*) malloc (sizeof(int));

Quando non è più necessaria, l’area allocata


dovrà essere HVSOLFLWDPHQWHOLEHUDWD tramite la
IXQ]LRQHfree()GHOODOLEUHULDVWGOLEK:
int *p = (int*) malloc(5*sizeof(int));
free(p);

3XQWDWRULH$OORFD]LRQH'LQDPLFD 13
$//2&$=,21(',1$0,&$'(//$
0(025,$H67587785(
L’allocazione dinamica di una variabile di tipo
struttura avviene nel modo consueto.
ESEMPIO
typedef struct {
char nome[20], cognome[20];
int eta;
long stipendio;
} persona;
persona *p;
p = (persona*) malloc (2*sizeof(persona));
Quest’ultima allocazione riserverà (nell’ipotesi
che un int occupi 2 byte e un long 4 byte)
2*(20+20+2+4) = 46 byte di memoria
L’area allocata è utilizzabile tramite uno degli
operatori di dereferenziamento (*, [] o, ->)
ESEMPIO
(*p).eta = 25; /* accede all’età di p[0] */
p[1].stipendio = 1500000L;
p -> eta = 26; /* accede all’età di p[0] */
ATTENZIONE alla precedenza degli operatori
++ p->eta; /* incrementa l’età di p[0]*/
(++p)->eta /* accede all’età di p[1] */

3XQWDWRULH$OORFD]LRQH'LQDPLFD 14
(6(03,2
Mostrare la differenza fra un vettore di interi
GHILQLWRVWDWLFDPHQWH e un vettore di interi
DOORFDWRGLQDPLFDPHQWH.
#include <stdio.h>
#include <stdlib.h>
main(){
int vector[15]; /* spazio per 15 interi */
int *dynVect; /* spazio per il punt. */
int k, i;
printf("Inserire la dimensione\n");
scanf("%d", &k);
dynVect = malloc(k * sizeof(int));

/* ora posso usare liberamente sia vector


sia dynVect come vettori, lunghi 15 e k
*/

/* i primi k quadrati */
for (i=0;i<k;i++)
dynVect[i] = i*i; /* o *(dynVect+i) */
/* i primi 15 pari */
for (i=0;i<15;i++)
vector[i] = 2*i;

free(dynVect);}

3XQWDWRULH$OORFD]LRQH'LQDPLFD 15
/¶$5($+($3
0DGDGRYHYLHQHTXHVWDPHPRULD"
La memoria allocata dinamicamente viene
allocata in un’apposita area, GLVWLQWDGDWXWWHOH
DOWUHILQTXLYLVWH O¶DUHDKHDS

/DPHPRULDDOORFDWDQHOORKHDSFRQ
malloc()
UHVWDULVHUYDWDILQRDFKHQRQYLHQH
HVSOLFLWDPHQWHGHDOORFDWDFRQfree()

In particolare, un’area allocata dentro a una


funzione VRSUDYYLYHDOODIXQ]LRQHFKHO¶KD
DOORFDWD, ed è disponibile in tutto il programma
(fino a quando non viene distrutta).

...
&2'(6(*0(17
'$7$6(*0(17

+($3


67$&.

3XQWDWRULH$OORFD]LRQH'LQDPLFD 16
(6(03,2
Scrivere una funzione che inserisce in un vettore
un numero n (non fissato a priori) di valori e
restituisca al main il puntatore al vettore creato.
L’INTESTAZIONE DELLA FUNZIONE
int* creaVett(void);

L’IMPLEMENTAZIONE DELLA FUNZIONE


#include <stdio.h>
#include <stdlib.h>
int* creaVett(void){
int *v, num, i;
printf("Quanti valori? ");
scanf("%d", &num);
v = (int*) malloc(num * sizeof(int));
for (i=0; i<num; i++){
printf("v[%d] = ",i); scanf("%d", &v[i]);
/* o anche: scanf("%d", v+i); */}
return v;}
IL CLIENTE
main(){
int *pv;
pv = creaVett();
/* Problema: non si sa quanti sono! */
...
free(pv);}

3XQWDWRULH$OORFD]LRQH'LQDPLFD 17
(6(03,2
Trasformare la funzione dell’esempio 2 in una
procedura, aggiungendo anche un parametro
che indichi la dimensione del vettore.
L’INTESTAZIONE DELLA PROCEDURA
La procedura restituisce il vettore creato
DWWUDYHUVRXQSDUDPHWUR: dunque, tale parametro
va passato SHUULIHULPHQWR
Poiché il tipo del vettore è un SXQWDWRUHa int
(int*), il tipo del parametro è un SXQWDWRUHD
SXQWDWRUHa int (int**)
void creaVett(int** pvett, int* pnum);

L’IMPLEMENTAZIONE DELLA PROCEDURA


#include <stdio.h>
#include <stdlib.h>
void creaVett(int** pv, int* pnum){
int i;
printf("Quanti valori? ");
scanf("%d", pnum);
*pv = (int*) malloc(*pnum * sizeof(int));
for (i=0; i< *pnum; i++){
printf("v[%d] = ", i);scanf("%d", *pv+i);
}
}

(6(03,2 VHJXH 
3XQWDWRULH$OORFD]LRQH'LQDPLFD 18
Scrivere una procedura per visualizzare un
vettore di interi, e aggiungerla al main() in modo
da vedere il vettore creato.
L’INTESTAZIONE DELLA PROCEDURA
void mostraVett(int* v, int dim);

L’IMPLEMENTAZIONE
#include <stdio.h>
#include <stdlib.h>
void mostraVett(int* v, int dim){
int i;
for (i=0; i<dim; i++)
printf("v[%d] = %d\n", i, v[i]);}

IL CLIENTE
main(){
int *pv, num;
creaVett(&pv, &num);
mostraVett(pv, num);
free(pv);
}
127$ pvqSDVVDWRD mostraVett()IRUPDOPHQWHSHU
YDORUH PD WUDWWDQGRVL GL XQ SXQWDWRUH O·DUHD GL
PHPRULDGDHVVRUHIHUHQ]LDWDQRQYLHQHGXSOLFDWD

(6(03,2

3XQWDWRULH$OORFD]LRQH'LQDPLFD 19
Scrivere una procedura vcopy() che, dato un
vettore di interi e la sua dimensione, ne crei un
altro uguale, copiando in quest’ultimo il contenuto
del primo.
L’INTESTAZIONE DELLA PROCEDURA
void vcopy(int* v1, int num, int** pv2);
o anche:
void vcopy(int v1[], int num, int (*pv2)[]);

L’IMPLEMENTAZIONE
#include <stdlib.h>
void vcopy(int* v1, int num, int** pv2){
int i;
*pv2 = (int*) malloc(num * sizeof(int));
for (i=0; i<num; i++) (*pv2)[i] = v1[i];}
IL CLIENTE
main(){
int vettore[] = {20,10,40,50,30};
int* newVet = NULL;
vcopy(vettore, 5, &newVet);
mostraVett(newVet, 5);
free(newVet);
}

3XQWDWRULH$OORFD]LRQH'LQDPLFD 20
(6(03,2
Riformulare l’esempio precedente definendo però
prima un tipo myvet come sinonimo di “vettore di
interi”.
typedef int* myvet;
IL PROBLEMA
Nel cliente, newVet era:
int* newVet = NULL;
mentre ora diventa:
myvet newVet = NULL;
4XHVWRSXzFRPSRUWDUHPRGLILFKHDOOH
GLFKLDUD]LRQLGHLSDUDPHWULGHOOHIXQ]LRQL
Più precisamente, prima avevamo:
void vcopy(int* v, int num, int** pv2);
void mostraVett(int* v, int dim);

mentre ora avremo:


void vcopy(myvet v, int num, myvet* ppv2);
void mostraVett(myvet v, int dim);
In pratica, myvet ha preso il posto di int*.

3XQWDWRULH$OORFD]LRQH'LQDPLFD 21
(6(03,2 VHJXH 
L’INTESTAZIONE DELLA PROCEDURA
void vcopy(myvet v, int num, myvet* ppv2);

L’IMPLEMENTAZIONE
void vcopy(myvet v, int num, myvet* pv2){
int i;
*pv2 = (myvet) malloc(num * sizeof(int));
for (i=0; i<num; i++) (*pv2)[i] = v1[i];}
IL CLIENTE
main(){
int vettore[5] = {20,10,40,50,30};
myvet newVet = NULL;
vcopy(vettore, 5, &newVet);
mostraVett(newVet, 5);
free(newVet);}

3XQWDWRULH$OORFD]LRQH'LQDPLFD 22

Potrebbero piacerti anche