OBIETTIVO DELLA LEZIONE
Puntatori
- Comprendere cosa è un puntatore e come possa essere utilizzato per accedere a vettori e matrici
- Essere in grado di utilizzare correttamente un puntatore sfruttando l'aritmetica dei puntatori
- Riconoscere gli errori nei programmi derivanti dalla non corretta applicazione dell'aritmetica dei puntatori
Formattazione e cast- Essere in grado di gestire correttamente l'output di numeri, vettori e stringhe
- Essere in grado di gestire correttamente la conversione tra tipi di dati diversi attraverso il cast
Tipi di dato strutturato- Comprendere l'importanza della progettazione dei tipi di dato come regola di buona programmazione
- Essere in grado di progettare ed impiegare correttamente tipi di dato definiti dall'utente
- Conoscere le modalità di accesso dei tipi di dato definiti dall'utente
Primo esercizio puntatori
Come mai la somma non sembra funzionare con i puntatori?
Come mai gli indirizzi p1 e p2 sono così diversi?#include <stdio.h> int i=2; // Questa è una variabile globale. Viene messa dal compilatore // nel segmento dati main() { double d=2.0; int * p1; double * p2; p1=&i; p2=&d; printf("\n"); printf("*p1 vale %d\n",*p1); printf("p1 vale %d\n",p1); printf(" ... mentre p1+1: %d\n\n",p1+1); printf("*p2 vale %f\n",*p2); printf("p2 vale %d\n",p2); printf(" ... mentre p2+1: %d\n\n",p2+1); }
s[3]='c';e
*(s+3*sizeof(char))='c';sono equivalenti. Scegliamo il primo modo!
Ogni variabile deve ricevere dalla scanf il tipo di dato corretto. Quindi scanf("%c" ... per i caratteri, scanf("%d" ... per gli interi, scanf("%f" ... per numeri con la virgola, ecc. Successivamente e' possibili visualizzarli come vogliamo MA dobbiamo usare sempre il giusto cast
// Ripasso Tipi di dato #include <stdio.h> #include <stdlib.h> int main() { char c; int i; float f; char s[256]; // carattere printf("metti un carattere -->") ; scanf("%c" , &c) ; // scanf con %c !!!! printf(" Hai messo il carattere --> %c <-- \n", c); printf(" visto come intero --> %d <-- \n", (int)c ); printf(" visto come float --> %f <-- \n", (float) c ); // intero printf("metti un intero -->") ; scanf("%d" , &i) ; // scanf con %d !!!! printf(" Hai messo l'intero --> %i <-- \n", i); printf(" visto come carattere --> %c <-- \n", (char) i ); printf(" visto come float --> %f <-- \n", (float) i ); // float printf("metti un numero con virgola -->") ; scanf("%f" , &f) ; // scanf con %f !!!! printf(" Hai messo il float --> %f <-- \n", f); printf(" visto come carattere --> %c <-- \n", (char) i ); printf(" visto come intero --> %f <-- \n", (int) i ); getchar(); getchar(); // non serve a nulla ma ferma la finestra... exit(0); }
quello che otteniamo dalla compilazione ed inserendo il carattere 'a', l'intero 97 e il numero con virgola 97.2345 e':
metti un carattere -->a Hai messo il carattere --> a <-- visto come intero --> 97 <-- visto come float --> 97.000000 <-- metti un intero -->97 Hai messo l'intero --> 97 <-- visto come carattere --> a <-- visto come float --> 97.000000 <-- metti un numero con virgola -->97.2345 Hai messo il float --> 97.234497 <-- visto come carattere --> a <-- visto come intero --> 97.234497 <--
Il tipo di dato int NON e' portabile!! Su ogni processore puo' essere implementato con un numero di bit diverso. Meglio usare long!
/ Ripasso Tipi di dato #include <stdio.h> #include <stdlib.h> int main() { char c; double d; long l; unsigned int ui; float f; c = 130; d = 130; l = 130; ui = 130; f = 130; printf("130 \n"); printf(" Come carattere : --> %c <-- \n", c); printf(" Come double : --> %d <-- \n", d); printf(" Come long : --> %d <-- \n", l); printf(" Come unsigned int : --> %u <-- \n", ui); printf(" Come float : --> %f <-- \n", f); // 2^31 -1 c = 2147483648 - 1; d = 2147483648 - 1; l = 2147483648 - 1 ; ui = 2147483648 - 1; f = 2147483648 - 1; printf("2^31 -1 \n"); printf(" Come carattere : --> %c <-- \n", c); printf(" Come double : --> %d <-- \n", d); printf(" Come long : --> %d <-- \n", l); printf(" Come unsigned int : --> %u <-- \n", ui); printf(" Come float : --> %f <-- \n", f); // 2^31 c = 2147483648 ; d = 2147483648 ; l = 2147483648 ; ui = 2147483648 ; f = 2147483648 ; printf("2^31 \n"); printf(" Come carattere : --> %c <-- \n", c); printf(" Come double : --> %d <-- \n", d); printf(" Come long : --> %d <-- \n", l); printf(" Come unsigned int : --> %u <-- \n", ui); printf(" Come float : --> %f <-- \n", f); // 2^32 - 1 c = 4294967296 - 1; d = 4294967296 - 1; l = 4294967296 - 1; ui = 4294967296 - 1; f = 4294967296 - 1; printf("2^32 - 1 \n"); printf(" Come carattere : --> %c <-- \n", c); printf(" Come double : --> %d <-- \n", d); printf(" Come long : --> %d <-- \n", l); printf(" Come unsigned int : --> %u <-- \n", ui); printf(" Come float : --> %f <-- \n", f); // 2^32 c = 4294967296 ; d = 4294967296 ; l = 4294967296 ; ui = 4294967296 ; f = 4294967296 ; printf("2^32 \n"); printf(" Come carattere : --> %c <-- \n", c); printf(" Come double : --> %d <-- \n", d); printf(" Come long : --> %d <-- \n", l); printf(" Come unsigned int : --> %u <-- \n", ui); printf(" Come float : --> %f <-- \n", f); // 2^32 + 1 c = 4294967296 + 1 ; d = 4294967296 + 1 ; l = 4294967296 + 1 ; ui = 4294967296 + 1 ; f = 4294967296 + 1 ; printf("2^32 + 1 \n"); printf(" Come carattere : --> %c <-- \n", c); printf(" Come double : --> %d <-- \n", d); printf(" Come long : --> %d <-- \n", l); printf(" Come unsigned int : --> %u <-- \n", ui); printf(" Come float : --> %f <-- \n", f); getchar(); getchar(); // non serve a nulla ma ferma la finestra... exit(0); }
130 Come carattere : --> é <-- Come double : --> 0 <-- Come long : --> 130 <-- Come unsigned int : --> 130 <-- Come float : --> 130.000000 <-- 2^31 -1 Come carattere : --> <-- Come double : --> -4194304 <-- Come long : --> 2147483647 <-- Come unsigned int : --> 2147483647 <-- Come float : --> 2147483648.000000 <-- 2^31 Come carattere : --> <-- Come double : --> 0 <-- Come long : --> -2147483648 <-- Come unsigned int : --> 2147483648 <-- Come float : --> 2147483648.000000 <-- 2^32 - 1 Come carattere : --> <-- Come double : --> -2097152 <-- Come long : --> -1 <-- Come unsigned int : --> 4294967295 <-- Come float : --> 4294967296.000000 <-- 2^32 Come carattere : --> <-- Come double : --> 0 <-- Come long : --> 0 <-- Come unsigned int : --> 0 <-- Come float : --> 4294967296.000000 <-- 2^32 + 1 Come carattere : --> ? <-- Come double : --> 1048576 <-- Come long : --> 1 <-- Come unsigned int : --> 1 <-- Come float : --> 4294967296.000000 <--
Rileggi velocemente il riassunto della lezione di stamattina che trovi in questa pagina (sezione Note). I costrutti presentati saranno molto utili per risolvere gli esercizi di oggi.
Esercizio 1 (Ripasso teoria puntatori)
Prova a rispondere alle seguenti domande.
Se non trovi le risposte o non sei sicuro, rivedi la lezione.
-------------------------------------------------------------------------------- 1. La memoria che viene impiegata da un programma è stata allocata dal: - programma stesso - dal sistema operativo - da nessuno dei due, è pronta dall'avvio della macchina -------------------------------------------------------------------------------- 2. In quanti segmenti è divisa la memoria allocata a disposizione dal programma? - 1 - 4 - 2 - 3 - 5 -------------------------------------------------------------------------------- 3. Le funzioni allocano i loro parametri formali: - sullo heap - sullo stack - nell'area del programma -------------------------------------------------------------------------------- 4. Il segmento di memoria che contiene il programma può crescere se il programma ha necessità? - No - Sì -------------------------------------------------------------------------------- 5. Avendo dichiarato int x[100]; che differenza c'è fra x e x[0]? - x è un indirizzo, mentre x[0] è il primo dato contenuto nell'array - Nessuna, sono entrambi riferimenti all'indirizzo prima cella -------------------------------------------------------------------------------- 6. Avendo dichiarato int x[100]; che differenza c'è fra x e &x[0]? - Nessuna, sono entrambi riferimenti all'indirizzo prima cella - Sono diversi in quanto uno punta all'array mentre l'altro e' l'indirizzo del primo elemento -------------------------------------------------------------------------------- 7. Avendo la seguente dichiarazione: float * p; si evince che p è: - nulla, la riga è sbagliata - un puntatore a float - un puntatore flottante - un puntatore con un cast a float -------------------------------------------------------------------------------- 8. Avendo definito int i; e int *pi; cosa vuole dire che è possibile fare in modo che pi punti a i? - Che pi è una cella che conterrà l'indirizzo della variabile i - Che saranno la stessa cosa e potrò intercambiarli nel codice.
Esercizio 2: Studio indirizzi di un vettore
Compila il seguente codice salvando il file con il nome di indirizzi_Vettore.c://indirizzi_Vettore.c #include <stdio.h> #include <stdlib.h> int main() { int i, x[100]; for(i=0; i<100; i++) x[i]=i+1; // riempiamo il vettore printf("Il valore di x e' %d\n", x); printf("Il valore di x[0] e' %d\n", x[0]); printf("L'indirizzo di x[0] e' %d\n", &x[0]); printf("Il valore di x[99] e' %d\n", x[99]); printf("L'indirizzo di x[99] e' %d\n", &x[99]); printf("La memoria occupata dal vettore e' %d\n", (void *)&x[99]+sizeof(int)-(void *)&x[0]); getchar(); return 0 ; }Quindi segui quanto di seguito elencato:soluzione.
- studia i valori delle printf stampati a monitor;
- controlla se la distanza in memoria fra gli elementi è quella che immaginavi
- verifica perché è stata usata la riga:
printf("La memoria occupata dal vettore e' %d\n", (void *)&x[99]+sizeof(int)-(void *)&x[0]);e non una semplice differenza fra indirizzi, ad esempio:&x[99] - &x[0]
Esercizio 3: Puntatori ed indirizzi
Per ognuna delle richieste di seguito elencate scrivere una singola riga di codice.
Assumi che siano già state dichiarate due variabili in virgola mobile n1 e n2, e che n1 sia stata inizializzata a 3,14.1. Dichiara fp che punti ad un tipo di dato float. 2. Assegna l'indirizzo della variabile n1 a fp. 3. Visualizza il valore dell'oggetto puntato da fp. 4. Assegna a n2 il VALORE dell'oggetto puntato da fp. 5. Visualizza il valore di n2. 6. Visualizza l'indirizzo di n1 (usare la printf con il parametro "%p") . 7. Visualizza l'indirizzo memorizzato in fp (usare la printf con il parametro "%p") e verifica se è uguale a quello del punto 6.soluzione.
Esercizio 4: Puntatori ed array
Scrivi una programma che presenti i seguenti requisiti:
1. dichiara un array A di 5 elementi interi 2. dichiara un puntatore ad intero p 3. associa l'indirizzo di inizio di A (A oppure &A[0] ) al puntatore p 4. inizializza questo array con i numeri crescenti 5,6,7,8,9 5. stampa gli elementi con il ciclo for indirizzando gli elementi dell'array con il classico modo A[i] nella printf 6. stampa nuovamente l'array A usando l'aritmetica dei puntatori, indirizzando gli elementi con *(p +i ) 7. verificare se, indirizzando gli elementi con *p + i, il programma funziona in modo corretto.
Suggerimento
La stampa degli elementi dell'array A puo' essere implementata mediante la rigaprintf("Elemento --> %d", A[i]); nel ciclo for.La stampa dell'array A con puntatori si ottiene mediante una porzione di codice del tipoprintf("Elemento --> %d", *(p +i ); nel ciclo for.Indirizzare gli elementi dell'array A con *p + i e' comunque corretto, nonostante in C l'operatore '*' abbia precedenza sull'operatore '+'. Questo e' pero' dovuto ad una pura casualita' di valori consecutivi degli elementi del vettore A (5,6,7,8,9) fanno si' che il risultato ottenuto con *(p+i) e *p +i sia lo stesso. Tuttavia, onde evitare spiacevoli errori difficilmente rilevabili, si consiglia di utilizzare SEMPRE le parentesi; in questo modo ne giova anche la leggibilita' del codice.
soluzione.
Esercizio 5: Tipi di dato e visualizzazione
Individua gli errori nelle istruzioni di seguito elencate:
1. per visualizzare il carattere 'c' printf("%s\n" , 'c' ); 2. per visualizzare il seguente numero in percentuale 4,345% printf("%.3f%" , 4,345 ); 3. per visualizzare il carattere della stringa "gennaro" printf("%c\n" , "gennaro" ); 4. printf(%d%d , 13, 17); 5. printf("%s\n" , 'MariaChiara' );soluzione.
Esercizio 6: Studio tipo di dato strutturato
Considera la seguente traccia:// MissItalia.c #include <stdio.h> #include <stdlib.h> typedef struct { char nome[256] ; char cognome[256] ; int voto[3]; // voto[0] e' il voto della giuria (da 0 a 100), // voto[1] quello della giuria speciale e voto[2] il voto // telefonico. int altezza; int peso; int cucina; // 0= aiuto!!! 10= apre un ristorante } MissItalia; // Iniziale maiuscola int main() { int i; MissItalia ragazza1; MissItalia * p1; MissItalia ragazza2; MissItalia * p2; p1 = &ragazza1; p2 = &ragazza2; // inseriamo la prima ragazza printf("Dammi il nome -->"); scanf("%s", p1->nome ); printf("Dammi il cognome -->"); scanf("%s", p1->cognome ); p1->altezza = 180; p1->voto[0] = 80; p1->voto[1] = 90; p1->voto[2] = 99; p1->peso = 78; p1->cucina = 0; // aiuto!!! // inseriamo la seconda ragazza printf("Dammi il nome -->"); scanf("%s", p2->nome ); printf("Dammi il cognome -->"); scanf("%s", p2->cognome ); p2->altezza = 180; p2->voto[0] = 85; p2->voto[1] = 85; p2->voto[2] = 90; p2->peso = 88; p2->cucina = 10; // meglio... // stampo i dati immessi printf("\n\n\n------------****------------\n"); printf("Nome:%s Cognome:%s \n", p1->nome , p1->cognome ); printf("altezza:%d Peso:%d \n", p1->altezza, p1->peso ); printf("voto: %d %d %d \n", p1->voto[0] , p1->voto[1] , p1->voto[2] ); printf("cucina [0=aiuto .. 10=professionista] = %d \n", p1->cucina ); // stampo i dati immessi printf("\n\n\n------------****------------\n"); printf("Nome:%s Cognome:%s \n", p2->nome , p2->cognome ); printf("altezza:%d Peso:%d \n", p2->altezza, p2->peso ); printf("voto: %d %d %d \n", p2->voto[0] , p2->voto[1] , p2->voto[2] ); printf("cucina [0=aiuto .. 10=professionista] = %d \n", p2->cucina ); // inserire qui il codice della comparazione }Scrivi il codice che:
- mostra le differenze di punteggio fra le due concorrenti i cui dati sono stati immessi;
- mostra la differenza di indirizzo di memoria fra p1 e p2.
soluzione.
Esercizio 7 (ripasso programmazione)
Scrivi il programma bisestile.c che chiede all'utente un numero e stampa a monitor se trattasi di un anno bisestile.
Considera come bisestile un anno divisibile per quattro tranne quelli secolari che non sono multipli di 400.
Attenzione: l'espressione (anno % 4) vale 0 se la variabile anno è divisibile per 4 senza resto.
soluzione.
Esercizio 8(ripasso programmazione)
Completa e salva il programma espressioni.c sottoriportato affinche' stampi con delle semplici printf il valore delle seguenti ESPRESSIONI :Il valore delle espressioni e' quello che prevedevi? Quali di loro hanno cambiato il valore delle variabili?
- (a + b + c + d)
- (a + b + c + d++)
- (a + b + c + ++d)
- (a = 7)
- (a == 8)
- (a *= 9)
- ( x[0] )
- ( x[0]==5 )
- ( x[a=11]==6 )
- ( 1,2,3,4 )
- ( 1,2,3, a=7 )
// espressioni.c #include <stdio.h> #include <stdlib.h> int main() { int a,b,c,d; int x[3]; a=1;b=1;c=1;d=1; x[0]=1; x[3]=1;x[2]=1; /* aggiungi il codice che serve per visualizzare il valore delle espressioni richieste */ return 0; }soluzione.