Scaricare la presentazione
La presentazione è in caricamento. Aspetta per favore
PubblicatoTore Nardi Modificato 10 anni fa
1
Laboratorio di Linguaggi lezione V Marco Tarini Università dellInsubria Facoltà di Scienze Matematiche, Fisiche e Naturali di Varese Corso di Laurea in Informatica Anno Accademico 2006/07
2
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Laboratorio di Linguaggi docente: Marco Tarini e-mail: tarini@isti.cnr.it ricevimento: Mercoledì dalle 11:00 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
3
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a E' arrivato il momento de... I PUNTATORI
4
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori: intro Una tipo variabile che contiene un indirizzo di una locazione di memoria: –l'indirizzo di un'altra variabile!
5
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori: sintassi della definizione int* pippo; " pippo " è una var di tipo int *, cioè puntatore ad intero int *pippo; " * pippo " (cioè il valore puntato da pippo) è una var di tipo intero o se preferite:
6
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori: sintassi dell'uso pippo il valore del puntatore. *pippo il valore dell'oggetto puntato. entrambi possono essere sia letti che assegnati (possono comparire da entrambi i lati di un assegnamento)
7
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori: significato int* pippo; 0x612A0230 0x612A022C 0x612A0228 0x612A0224 0x612A0220 0x612A021C 0x612A0218 0x612A0214 00 00 00 FF 01 22 00 AB 21 00 00 00 12 23 D2 FF FF 02 41 A4 61 2A 02 1C 00 00 00 A0 12 33 A3 D0 variabiletipolocazione pippoint* 0x612A22C indirizzo pippo (il puntatore stesso) vale... *pippo (la variabile puntata da pippo) vale... *pippo pippo m e m o r i a (il puntatore stesso) vale... 0x612A021C (la variabile puntata da pippo) vale... 0x000000A0
8
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Cambiare il valore del puntatore pippo++; 0x612A0230 0x612A022C 0x612A0228 0x612A0224 0x612A0220 0x612A021C 0x612A0218 0x612A0214 00 00 00 FF 01 22 00 AB 21 00 00 00 12 23 D2 FF FF 02 41 A4 61 2A 02 1C 00 00 00 A0 12 33 A3 D0 variabiletipolocazione pippoint* 0x612A22C indirizzo *pippo pippo +4 61 2A 02 20 pippo *pippo (il puntatore stesso) vale... 0x612A021C (la variabile puntata da pippo) vale... 0x000000A0 m e m o r i a
9
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Cambiare il valore del puntatore pippo++; 0x612A0230 0x612A022C 0x612A0228 0x612A0224 0x612A0220 0x612A021C 0x612A0218 0x612A0214 00 00 00 FF 01 22 00 AB 21 00 00 00 12 23 D2 FF FF 02 41 A4 61 2A 02 1C 00 00 00 A0 12 33 A3 D0 variabiletipolocazione pippoint* 0x612A22C indirizzo *pippo pippo 61 2A 02 20 pippo *pippo (il puntatore stesso) vale... 0x612A021C (la variabile puntata da pippo) vale... 0x000000A0 0x612A0220 0x1223D2FF m e m o r i a
10
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Algebra dei Puntatori equivalente a double *p;... p[ 5 ] *(p + 5)
11
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Zucchero sintattico typedef struct { char nome[24]; char cognome[24]; int peso; } Persona; Persona *p;... *p.peso come accedo al campo peso della Persona puntata da p? (*p).peso p->peso o, equivalentemente, con l'apposito operatore "freccina": p.peso* () interpretato come
12
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Zucchero sintattico typedef struct { char nome[24]; char cognome[24]; int peso; } Persona; void pippo(Persona p) {... if (p.peso ==... )... } typedef struct { char nome[24]; char cognome[24]; int peso; } Persona; void pippo(Persona* p) {... if (p->peso ==... )... }
13
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a 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!
14
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Assegnare i Puntatori Modo 1: prendere l'indirizzo di una variabile esistente –il puntatore punterà a quella variabile Operatore "ampersand" ( ) Esempio: & double d = 9.0; double *p; p = &d; *p = 21.5; printf("%f",*p); il puntatore p punta all'indirizzo di memoria dove vive la variabile d scrivi il valore di *p. Cosa scrive? printf("%f",d); scrivi il valore di d. Cosa scrive?
15
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Operatore & e i tipi se y è una var di tipo T... & y T T*
16
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Operatore & e vettori int numeri[]={10,20,30,40}; int *punt; punt = & numeri; errore di tipo! numeri non è mica di tipo int ! (e quindi &numeri non è di tipo int* ) punt = & (numeri[0]); scrivere invece: oppure anche: punt = numeri; int i; for (i=0; i<4; i++) { printf("%d ", numeri[i]); } scriviamo tutti e 4 i numeri: int i; for (i=0; i<4; i++) { printf("%d ", *(punt++)); } usando i puntatori:
17
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Altro Esempio 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++; } 'p' 'u' 'n' 't' 'a' 't' 'o' 'r' 'e' 0 stringa[0] stringa[1] stringa[2] stringa[3] stringa[4] stringa[5] stringa[6] stringa[7] stringa[8] stringa[9] stringa p p p p p p p p p p
18
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Uso dei Puntatori come Parametri vi ricordate quel problemino? void raddoppia (int x) { x = x * 2; } 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 !
19
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Uso dei Puntatori come Parametri un'altra motivazione possibile: efficienza int eta_fra_10_anni (Persona pp) { return pp.eta + 10; } int eta_fra_10_anni (Persona * pp) { return pp->eta + 10; } ogni volta che si chiama questa funzione, vengono copiati sizeof(Persona) bytes. ogni volta che si chiama questa funzione, vengono copiati sizeof(Persona*) bytes. typedef struct { char nome[20]; char cognome[20]; int eta; Esami* esami_sostenuti[50]; } Persona;
20
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Uso dei Puntatori come Parametri un'altra motivazione possibile: efficienza int eta_fra_10_anni (Persona pp) { return pp.eta + 10; } int eta_fra_10_anni (Persona * pp) { return pp->eta + 10; } inefficiente efficiente 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) In questi casi, però, meglio aggiungere anche la keyword const : int eta_fra_10_anni (const Persona * pp) { return pp->eta + 10; }
21
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Uso dei Puntatori come Parametri Riassumendo: void Procedura( TipoParametro x) {... } void Procedura( TipoParametro* x) {... } void Procedura( const TipoParametro* x) {... } passaggio di parametro per copia (l'unico possibile in C) 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
22
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Uso dei Puntatori come Parametri Riassumendo: void Procedura( TipoParametro x) {... } void Procedura( TipoParametro* x) {... } void Procedura( const TipoParametro* x) {... } usare quando la procedura non deve cambiare il valore del parametro usare quando la procedura deve cambiare il valore del parametro usare quando la procedura non deve cambiare il valore del parametro...ma sarebbe troppo oneroso fare la copia del parametro (parametro voluminoso!) (chiaramente, lo stesso vale per le funzioni)
23
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a 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!
24
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Allocazione void* malloc(unsigned int n); funzione malloc ( sta per 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 senza tipo, in pratica, un semplice indirizzo di memoria void*
25
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Allocazione: esempio int* p; p = malloc( ? ); 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! int* p; p = (int*) malloc(4); Soluzione: basta fare un type-cast: 4
26
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a 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 Il valore resituito dalle malloc va controllato ! int* p; p = (int*) malloc(4); if (p == NULL) { /* finita memoria... */ } oppure, più coincisamente if (!p) {
27
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Allocazione 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! typedef struct { /*blah blah... un sacco di campi, array...*/ } TipoStrano TipoStrano* p; p = (TipoStrano *) malloc(sizeof(TipoStrano));
28
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a 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 !
29
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Allocazione e Deallocazione: esempio int proc() { int k; k=15;... /* lavora con k */ return 0; }; int proc() { int* k; k = (int*)malloc( sizeof(int) ); *k = 15;... /* lavora con *k */ free(k); return 0; }; k viene automaticamente allocato (i 4 bytes di memoria necessari al suo immagazzinamento vengono "prenotati"). k viene inizializzato (a 15) 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 usando l'allocazione dinamica:
30
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Vi ricordate di 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.
31
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a 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.
32
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Allocazione di vettori Ricordiamoci sempre: int* v = (int*) calloc(100000,sizeof(int) ); int v[100000]; A) fixed size vector: B) vettore allocato dinamicamente: In entrambi i casi: ho un vettore di 100000 interi posso scrivere ad esempio: v[2]= v[0] + 3 * v[1] ;
33
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Allocazione di vettori Ricordiamoci sempre: int* v = (int*) calloc(100000,sizeof(int) ); int v[100000]; A) fixed size vector: B) vettore allocato dinamicamente: Differenza 1: dimensione variabile se x è una var intera, posso scrivere: ma non posso scrivere: int* v = (int*) calloc(x,sizeof(int) ); int v[x]; qua è richiesta una costante!
34
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Allocazione di vettori Ricordiamoci sempre: int* v = (int*) calloc(100000,sizeof(int) );... /* usa v */ free(v); int v[100000]; A) fixed size vector: B) vettore allocato dinamicamente: Differenza 2: se ho allocato, devo deallocare int* v = (int*) calloc(100000,sizeof(int) );
35
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Differenza 3: fixed size = più efficiente il solito prezzo da pagare per l'uso dei puntatori......maggiorato mettiamo che v valga 0xAA000000 : se fixed: se dinamco: Allocazione di vettori Ricordiamoci sempre: v[2] int v[100000]; A) fixed size vector: B) vettore allocato dinamicamente: int* v = (int*) calloc(100000,sizeof(int) ); compilazione v[2] READ TEMP0 0xAA000000 ADD TEMP0 8 READ TEMP TEMP0 READ TEMP 0xAA000008 0xAA000000 + 2 x sizeof(int) ma precalcolato staticamente
36
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Differenza 4: vengono allocati in zone diverse della memoria... come vedremo nella lezione sulla gestione della memoria Allocazione di vettori Ricordiamoci sempre: int v[100000]; A) fixed size vector: B) vettore allocato dinamicamente: int* v = (int*) calloc(100000,sizeof(int) );
37
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori: operazioni a basso livello Esercizio: 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 ? un numero in virgola mobile a doppia precisione di solito, ma dipende dall'implementazione
38
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori: operazioni a basso livello spazio riservato per la variabile d (8 bytes) int main() { 1B903C32 1B903C33 1B903C34 1B903C35 1B903C36 1B903C37 1B903C38 1B903C39 1B903C3A 1B903C3B 1B903C3C dobule d;
39
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori: operazioni a basso livello 1B903C32 1B903C33 1B903C34 1B903C35 1B903C36 1B903C37 1B903C38 1B903C39 1B903C3A 1B903C3B 1B903C3C spazio riservato per la variabile d (8 bytes) c o d i f i c a d i 3. 1 4 1 5 9 2 5 6 int main() { dobule d; d = 3.14159256; Byte * v ; 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)
40
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a v = & d; Puntatori: operazioni a basso livello 1B903C32 1B903C33 1B903C34 1B903C35 1B903C36 1B903C37 1B903C38 1B903C39 1B903C3A 1B903C3B 1B903C3C spazio riservato per la variabile d (8 bytes) c o d i f i c a d i 3. 1 4 1 5 9 2 5 6 int main() { dobule d; d = 3.14159256; Byte * v ; v [0] v [1] v [2] v [3] v [4] v [5] v [6] v [7] for (i=0; i<8; i++) printf("%d,",v[i]); v = (Byte*) & d; for (i=0; i< sizeof(double); i++) printf("%d,",v[i]); int i; };
41
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a 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 10 23 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?
42
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a 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;
43
M a r c o T a r i n i L a b o r a t o r i o d i L i n g u a g g i 2 0 0 6 / 0 7 U n i v e r s i t à d e l l I n s u b r i a Puntatori Molto potenti... "Ad un grande potere corrisponde una grande responsabilità." – lo zio di Spiderman
Presentazioni simili
© 2024 SlidePlayer.it Inc.
All rights reserved.