unità 6 - dis.uniroma1.it · puntatori corso di fondamenti di informatica ingegneria delle...

53
Puntatori Corso di Fondamenti di Informatica Ingegneria delle Comunicazioni – BCOR Ingegneria Elettronica – BELR Unità 6 Domenico Daniele Bloisi

Upload: dolien

Post on 15-Feb-2019

220 views

Category:

Documents


0 download

TRANSCRIPT

PuntatoriCorso di Fondamenti di InformaticaIngegneria delle Comunicazioni – BCORIngegneria Elettronica – BELR

Unità 6

Domenico Daniele Bloisi

Docenti

Parte I – prof. Silvio [email protected]@dis.uniroma1.it

http://www.dis.uniroma1.it/~salza/fondamenti.htm

Parte II – ing . Domenico Daniele Bloisi, [email protected]

http://www.dis.uniroma1.it/ ~bloisi /didattica /fondinf1112.htmlhttp://www.dis.uniroma1.it/ ~bloisi /didattica /fondinf1112.html

Nota: %7E corrisponde alla tilde ~

2011/2012RicorsioneUnità 6

Pagina 2

Informazioni Generali

ing. Domenico Daniele Bloisi, PhD

Dipartimento di Informatica e SistemisticaVia Ariosto 25Via Ariosto 25(adiacente Piazza Dante,

A fermate Manzoni, Vittorio Emanuele,Tram 3 fermata via Labicana)

mailto:[email protected]

http://www.dis.uniroma1.it/~bloisi

2011/2012PuntatoriUnità 6

Pagina 3

Ricevimento

Su appuntamento. Inviare una email per conferma.

DIS, via Ariosto 25DIS, via Ariosto 25II piano, stanza B211

Si consiglia di controllare la bacheca degli avvisihttp://www.dis.uniroma1.it/~bloisi/didattica/fondin f1112.html#Avvisi

2011/2012PuntatoriUnità 6

Pagina 4

Sommario – Unità 6

• Memoria, indirizzamento e puntatori• Tipo void * e conversioni sui puntatori• Gestione dinamica della memoria• Gestione dinamica della memoria• Tempo di vita delle variabili allocate dinamicamente

• Problemi di deallocazione della memoria• Passaggio dei parametri tramite puntatori

Pagina 52011/2012PuntatoriUnità 6

Memoria, indirizzamento e puntatori

L’accesso memoria di un calcolatore avviene attraverso un meccanismo di indirizzamento.

In C il programmatore ha la possibilità di gestire gli indirizzi attraverso delle variabili che vengono definite di tipo puntatore .

I valori delle variabili di tipo puntatore sono

Pagina 62011/2012PuntatoriUnità 6

I valori delle variabili di tipo puntatore sono indirizzi di memoria , ossia dei valori numerici che fanno riferimento a specifiche locazioni di memoria.

Notazione grafica

L’indirizzamento si rappresenta graficamente tramite una freccia.

Pagina 72011/2012PuntatoriUnità 6

Spesso non occorre conoscere lo specifico valore di una variabile di tipo puntatore (es. 00100).

Operatore indirizzo-di

Per ottenere dei valori di tipo puntatore, cioè degli indirizzi, si utilizza l’operatore &

int i = 1;printf("L\' indirizzo di i e\' "

"%p\n", &i);printf("mentre il valore di i e\' "

"%d\n", i);

stampa

Pagina 82011/2012

stampa

L' indirizzo di i e' 0028FF1Cmentre il valore di i e' 1

PuntatoriUnità 6

Operatore indirizzo-di

L’operatore & si chiama operatore indirizzo-di e restituisce l’indirizzo della variabile a cui viene applicato.applicato.

Nota:i = &10;non è ammesso dato che 10 è una costante int . Analogamente non si può accedere all’indirizzo di una espressione.

Pagina 92011/2012PuntatoriUnità 6

Nota: l’indicatore di conversione per un indirizzo è %p (il formato è definito dall’implementazione)

una espressione.

Operatore di dereferenziamento (indirizzamento indiretto)Quando si considera una variabile di tipo puntatore , l’operatore * permette di recuperare il valore della locazione di memoria puntata.locazione di memoria puntata.

int i, j = 1;i = *&j;

L’istruzione i = *&j; assegna (con una notazione un po’ complicata) il valore della variabile j alla variabile i . Equivale, in pratica, all’istruzione i = j;

Pagina 102011/2012

Equivale, in pratica, all’istruzione i = j;

PuntatoriUnità 6

L’operatore (unario) di indirizzamento indiretto * no n deve essere confuso con l’operatore di moltiplicazio ne (binario).

Operatore di dereferenziamento (indirizzamento indiretto)int i, j = 1;

00100 00200

i = *&j;

?i 1j

&j ≡ 00200

Valore contenuto in 00200 ≡ 1*( &j ) ≡

Pagina 112011/2012PuntatoriUnità 6

Valore contenuto in 00200 ≡ 1*( &j ) ≡

1i

00100

1j

00200

Operatore & VS operatore *

Se x è una variabile, &x denota l’indirizzo in memoria di tale variabile.

&x ≡ α&x ≡ α

x

αnome

indirizzo

Pagina 122011/2012PuntatoriUnità 6

Se α è l’indirizzo in memoria di una variabile, * α denota tale variabile:

* α ≡ x

Variabili di tipo puntatore

Per la gestione degli indirizzi occorre dichiarare de lle variabili di tipo puntatore, specificando il tipo d ella locazione di memoria puntata.locazione di memoria puntata.

int *p1;... // allocazione di memoria (vedi dopo)*p1 = 10;

dichiara una variabile di tipo puntatore ad intero ed

Pagina 132011/2012

dichiara una variabile di tipo puntatore ad intero ed assegna alla variabile puntata il valore 10.

PuntatoriUnità 6

Attenzione

1. La dichiarazione di una variabile puntatore non alloca memoria per la variabile puntata ;prima di accedere alla variabile puntata prima di accedere alla variabile puntata bisogna allocare esplicitamente memoria(vedremo dopo come …)

2. Nelle dichiarazioni multiple tipo:

int *p1, p2;

Pagina 142011/2012

int *p1, p2;

p2 non è un puntatore!

PuntatoriUnità 6

Esempio: uso di variabili puntatore

int i, j, k;int *pt_i, *pt_j;pt_i = &i;pt_i = &i;pt_j = &j;i = 1;j = 2;k = *pt_i + *pt_j;*pt_i = 10;printf("i = %d \ n", i);

Pagina 152011/2012

printf("i = %d \ n", i);printf("k = %d\n", k);

PuntatoriUnità 6

Diagramma della memoria

Le variabili di tipo int i , j , k , vengono manipolateattraverso i puntatori alle locazioni di memoria ad esse associate al momento della dichiarazione.associate al momento della dichiarazione.

Pagina 162011/2012PuntatoriUnità 6

Esecuzione

Il programma stampa a video:

i = 10i = 10k = 3

Pagina 172011/2012PuntatoriUnità 6

Assegnazione di un valore specifico a puntatore#include <stdio.h>

int main () {

Output del compilatore

int main () {int *ptr;ptr = 1000;*ptr = 5;printf("%d\n", *ptr);

}

Cosa stampa questo codice?

Pagina 182011/2012PuntatoriUnità 6

Output del compilatore

warning: assignment makes pointer from integer without a cast

L’ assegnazione di un indirizzo specifico ad una variabile puntatore è da evitare poiché

Assegnazione di un valore specifico a puntatore

una variabile puntatore è da evitare poiché può causare il crash del programma.

Pagina 192011/2012PuntatoriUnità 6

Accessi in memoria

int i, j = 2, *pt;pt = &j;i = *pt;i = *pt;

per ottenere il valore da assegnare a i devono essere fatti 2 accessi in memoria:

• il primo accesso viene fatto all’indirizzo di pt , per recuperare il dato ivi memorizzato, i.e. l’indirizzo di j

Pagina 202011/2012

• il secondo accesso avviene all’indirizzo di j , per recuperare il valore memorizzato nella variabile j

PuntatoriUnità 6

Esempio puntatoriConsideriamo gli effetti del seguente codice:

int *pointer; // dichiara pointer come un // puntatore a int// puntatore a int

int x = 1, y = 2;pointer = &x; // (1) assegna a pointer l'indirizzo

// di x (i.e., pointer punta x)y = *pointer; // (2) assegna a y il contenuto di

// pointerx = pointer; // (3) assegna ad x l'indirizzo

Pagina 212011/2012PuntatoriUnità 6

x = pointer; // (3) assegna ad x l'indirizzo // contenuto in pointer

*pointer = 3; // (4) assegna alla variabile // puntata da pointer il valore 3

Diagramma della memoria

1x32456

1x32456

?

2y

pointer

54327

12098

(1)

32456

2y

pointer

54327

12098pointer = &x;

Pagina 222011/2012PuntatoriUnità 6

?pointer 32456pointer

Diagramma della memoria

1x32456

1x32456

32456

2y

pointer

54327

12098

(2)

32456

1y

pointer

54327

12098y = *pointer;

Pagina 232011/2012PuntatoriUnità 6

32456pointer 32456pointer

Diagramma della memoria

1x32456

32456x32456

32456

1y

pointer

54327

12098

(3)

32456

1y

pointer

54327

12098x = pointer;

Pagina 242011/2012PuntatoriUnità 6

32456pointer 32456pointer

Diagramma della memoria

32456x32456

3x32456

32456

1y

pointer

54327

12098

(4)

32456

1y

pointer

54327

12098*pointer = 3;

Pagina 252011/2012PuntatoriUnità 6

32456pointer 32456pointer

Ricapitolando

Possibili valori ottenibili tramite l’utilizzo di variabili puntatore:

pointer valore della variabile puntatore (i.e., l’indirizzo della locazione di memoria a cui punta)

&pointer indirizzo fisico della locazione di memoria del puntatore

Pagina 26

*pointer valore contenuto nella locazione di memoria a cui punta il puntatore

PuntatoriUnità 6

2011/2012

Operazioni sui puntatori

A valori di tipo puntatore si applicano le operazion i del tipo int .Particolarmente utile è l’operazione di incrementoParticolarmente utile è l’operazione di incremento

int *pti;...pt++;

che consente di puntare alla successiva locazione di tipo int .

Pagina 272011/2012

tipo int .

L’uso di queste operazioni verrà approfondito nella Unità 7 – Array e Matrici

PuntatoriUnità 6

Puntatori costanti

La specifica const può essere applicata anche a variabili di tipo puntatore.

double pi = 3.5;double const *pt = &pi ;(*pt )++; // OK (pi vale 4.5)pt++; // NO (pt e’ costante)

Pagina 282011/2012

In questo caso, la specifica const si applica al puntatore, ma non alla variabile puntata.

PuntatoriUnità 6

Puntatori a costanti

Si può anche specificare che un puntatore debba puntare a costanti.

const int k = 3;const int k = 3;const int *pt; // dichiarazione di puntatore

// a costantept = &k; // OKpt++; // ammesso anche se non si sa cosa

// vada a puntare ptint *pti ;

Pagina 292011/2012

int *pti ;pti = &k; // NO

PuntatoriUnità 6

Puntatori a puntatori

Come per ogni altro tipo si può definire un puntato re ad una variabile di tipo puntatore.

double x;double x;double * pt;double ** ptpt;x = 4;pt = &x;ptpt = &pt;printf("%f \ n", **ptpt);

Cosa stampa questo frammento di codice?

Pagina 302011/2012PuntatoriUnità 6

printf("%f \ n", **ptpt);

Diagramma della memoria

stampa il valore di x .

Pagina 312011/2012PuntatoriUnità 6

Il puntatore NULL (1/3)

Le variabili di tipo puntatore possono assumere anc he un valore speciale: NULL

Questo valore serve a specificare che la variabile non punta alcuna locazione di memoria .

In C tale valore in genere corrisponde allo 0, ma s i raccomanda di usare NULL , in particolare per verificare che ad una variabile puntatore non sia associato un o specifico riferimento.

Pagina 322011/2012

specifico riferimento.

PuntatoriUnità 6

NULL è una costante simbolica in genere definita in <stdio.h>

Il puntatore NULL (2/3)

Si faccia attenzione a non confondere variabili il cui valore è NULL con variabili non inizializzate:inizializzate:

una variabile non inizializzata non ha alcun valore, neanche NULL.

Il confronto con NULL può essere usato in una condizione di un’istruzione if - else , for , etc.

Pagina 332011/2012

condizione di un’istruzione if - else , for , etc.

PuntatoriUnità 6

Il puntatore NULL (3/3)

Esempio

int *pt = NULL;if (pt != NULL)if (pt != NULL)

*pt = 10;

In questo caso il ramo if non viene eseguito.

L’istruzione *pt = 10; eseguita al momentoin cui pt vale NULL genererebbe un errore a tempo di

Pagina 342011/2012

in cui pt vale NULL genererebbe un errore a tempo di esecuzione.

PuntatoriUnità 6

Il tipo void* (1/2)

Mentre nel caso delle dichiarazioni dei tipi primiti vi è indispensabile definire il tipo della variabile per consentire al compilatore di allocare la memoria consentire al compilatore di allocare la memoria necessaria, nel caso dei puntatori, la memoria per il puntatore è fissa (corrisponde alla dimensione di un indirizzo di memoria) e quindi si può omettere la specifica del tipo della variabile puntata.

void *pt ;int i;

Pagina 352011/2012

int i;pt = &i ;

PuntatoriUnità 6

Il tipo void* (2/2)

In questi casi:

• non sono più ammesse le operazioni sui • non sono più ammesse le operazioni sui puntatori

• il puntatore assegnato ad una variabile void*non può essere assegnato ad una variabile di tipo puntatore (ad un tipo definito).

Pagina 362011/2012PuntatoriUnità 6

tipo puntatore (ad un tipo definito).

Conversioni su puntatori

Anche nel caso delle variabili di tipo puntatore so no possibili conversioni esplicite:

void * pt;int i;pt = &i;int * pti;pti = (int*)pt; // il valore viene convertito

// a puntatore a int

Pagina 372011/2012

Tuttavia, l’utilizzo delle conversioni sui puntatori è sconsigliato in quanto spesso provoca degli errori di programmazione.

PuntatoriUnità 6

Gestione dinamica della memoria

Nell’ Unità 5 è stato presentato il modello di allo cazione della memoria tramite stack , che si basa sulle regole di campo d’azione (definito staticamente , cioè al momento campo d’azione (definito staticamente , cioè al momento della compilazione).

Attraverso i puntatori, il C permette di utilizzare u n altro modello di allocazione della memoria, che consente d i definire la memoria dinamicamente , cioè al momento dell’ esecuzione del programma.

Pagina 382011/2012

Questa possibilità risulta particolarmente utile ed importante quando non sono note o prevedibili a pri orile dimensioni dei dati in ingresso ad un programma.

PuntatoriUnità 6

L’operatore sizeof (1/2)

Il numero di byte occupati da una variabile è dato dall’applicazione di sizeof .

Esempio

sizeof(a) è il numero di byte occupati dalla variabile a

Pagina 392011/2012PuntatoriUnità 6

L’operatore sizeof può essere applicato ad un tipo, ad un nome di variabile o ad una costante

L’operatore sizeof (2/2)

Restituisce la dimensione in byte dell’oggetto passato come parametro

• tale calcolo viene effettuato in compilazione in base al tipo di dato che viene passato a sizeof

• se si incrementa un puntatore p, il suo valore numerico

Pagina 402011/2012

• se si incrementa un puntatore p, il suo valore numerico (indirizzo in memoria in byte) verrà incrementato di sizeof(*p)

PuntatoriUnità 6

stampa indirizzo, occupazione di memoria e valore

int main (void) {int a = 12;char b = 'a';char b = 'a';float c = 0.1243;printf ("Indirizzo di a e\' %x, occupa %d bytes,"

" il suo valore e\' %d\n", &a, sizeof(a), a);printf ("Indirizzo di b e\ ' %x, occupa %d bytes,"

" il suo valore e\' %c\n", &b, sizeof(b), b);printf ("Indirizzo di c e\' %x, occupa %d bytes,"

" il suo valore e \ ' %f \ n", &c, sizeof(c), c);

Pagina 412011/2012

" il suo valore e \ ' %f \ n", &c, sizeof(c), c);return 0;

}

PuntatoriUnità 6

%x intero esadecimale senza segno

Esecuzione

Esercizio 6.1Si modifichi il codice precedente in modo da stampa re la parola “byte” se l’occupazione è pari ad 1, mentre “byt es”

Pagina 422011/2012PuntatoriUnità 6

parola “byte” se l’occupazione è pari ad 1, mentre “byt es” se l’occupazione è > 1

Es. Indirizzo di b e' 28ff1b, occupa 1 byte, il suo valore e' a

Differenza tra valore e indirizzo di una variabile� L’indirizzo è dato in forma numerica (ma

non di tipo numerico) ed è assegnato dal compilatorecompilatore

� Il valore è assegnato dal programma

� Tutte le variabili dello stesso tipo occupano lo stesso numero di byte

Pagina 432011/2012PuntatoriUnità 6

stesso numero di byte• Hanno la medesima rappresentazione interna

Applicazione di sizeof (1/2)

Essendo un operatore , sizeof può essere utilizzatoponendo l’operando tra parentesi oppure anche senzal’utilizzo delle parentesil’utilizzo delle parentesi

Le seguenti istruzioni sono equivalenti tra loro:

char a = 'r';int size_a ;size_a = sizeof a;

Pagina 442011/2012

size_a = sizeof a;size_a = sizeof (a);

PuntatoriUnità 6

Applicazione di sizeof (2/2)

Un’eccezione a questa possibilità si ha nel caso seguente:

int size_float;

size_float = sizeof(float); // espressione // valida

size_float = sizeof float; // errore in // compilazione

Pagina 452011/2012

// compilazione

quando l’operando di sizeof è il nome di un tipo di dato(float , nell’esempio), le parentesi sono obbligatorie.

PuntatoriUnità 6

Allocazione dinamica della memoria

Durante l’esecuzione, un programma può richiedere esplicitamente uno spazio di memoria per immagazzinare dati.dati.Allo stesso modo, può richiedere di rilasciare tale spazio quando non sarà più necessario.

Il C offre la funzione malloc per riservare (allocare) uno spazio di memoria e la funzione free per rilasciare (deallocare) memoria.

Pagina 462011/2012

free per rilasciare (deallocare) memoria.

Insieme all’operatore sizeof sono essenziali per l’allocazione dinamica della memoria.

PuntatoriUnità 6

Funzione malloc

La funzione malloc permette di allocare dinamicamente una porzione (chunk) di memoria.

void *malloc( size_t size );

Restituisce un puntatore alla porzione di memoria di dimensione size oppure NULL se si è verificato un errore.E’ definita nella standard library - va inserita la direttiva#include <stdlib.h>

Pagina 472011/2012

#include <stdlib.h>

size_t è definito come il tipo intero senza segno restituito dall’operatore sizeof

PuntatoriUnità 6

Esempio malloc

int *p;• crea in memoria una variabile di tipo puntatore a int

p = malloc(sizeof(int));• crea in memoria una variabile di tipo int• restituisce l’indirizzo della variabile creata (prim o byte)• assegna l’indirizzo restituito a p

*p = 7;• la variabile di tipo int creata assume il valore 7

Pagina 482011/2012

• la variabile di tipo int creata assume il valore 7

printf("%d", *p);• viene stampato a video 7

PuntatoriUnità 6

Modello di allocazione dinamica

L’allocazione dinamica della memoria avviene nello heap.Se lo spazio di memoria allocabile dinamicamente è e saurito viene restituito il puntatore NULL.

int *pt1; // dichiarazione del puntatore ad intpt1 = malloc(sizeof(int)); // creazione dinamica

// della variabile intif (pt1 == NULL) {

printf("allocazione fallita\n");exit(EXIT_FAILURE);

}

Pagina 492011/2012

}

PuntatoriUnità 6

In caso di mancanza di memoria il programma termina con l’istruzione exit(EXIT_FAILURE); che notifica il fallimento.

Funzione free

La funzione free permette di deallocare la memoria allocata dinamicamente.

void free( void* ptr );

free dealloca lo spazio puntato da ptr , rendendolodisponibile per usi futuri.

Nota: ptr deve essere stato usato in una chiamata

Pagina 502011/2012

Nota: ptr deve essere stato usato in una chiamataprecedente a malloc()

E’ definita nella standard library ( <stdlib.h> )

PuntatoriUnità 6

Esempio free#include <stdio.h>#include <stdlib.h>

int main() {int main() {int *p;double *d;p = malloc(sizeof(int));*p = 7;printf("%d", *p);free(p);d = malloc(sizeof(double));

Pagina 512011/2012

d = malloc(sizeof(double));*d = 7;printf(" %f", *d);free(d);

}PuntatoriUnità 6

Esercizi

Esercizio 6.2

Scrivere un programma che legga 10 numeri interi e restituisca il minimo, usando variabili di tipo pun tatore restituisca il minimo, usando variabili di tipo pun tatore ad int anziché variabili di tipo int .

Esercizio 6.3

Scrivere il programma dell’esercizio precedente tram ite allocazione dinamica della memoria. Deallocare la memoria utilizzata prima della terminazione del

Pagina 522011/2012

memoria utilizzata prima della terminazione del programma.

PuntatoriUnità 6

Esercizi

Esercizio 6.4

Scrivere una funzione che dato in ingresso un puntatore ne stampi la dimensione in byte, il valor e, puntatore ne stampi la dimensione in byte, il valor e, l’indirizzo di memoria ed il valore della variabile puntata. Scrivere un programma che ne verifichi il comportamento.

Pagina 532011/2012PuntatoriUnità 6