Lezione 9
Transcript
Lezione 9
Esercitazione 9 Array come puntatori Aritmetica dei puntatori Array multidimensionali Ricapitolazione su Ricorsione e Array Stringhe Array come puntatori Gli array e i puntatori in C sono strettamente correlati. Il nome di un array infatti è un puntatore al suo primo elemento. Più precisamente, il nome di un array è una costante - si noti bene: una costante, non una variabile ! il cui valore è l’indirizzo del primo elemento dell’array. Esempio char tab [100]; char *s; s = & tab [0]; s = tab; /* questi due assegnamenti sono perfettamente equivalenti */ Array come puntatori Così come gli elementi di un array vengono scanditi per mezzo dell’indice, si può accedere agli elementi di un array per mezzo di un puntatore. Esempio: Cosa stampa ? char tab [100]; char *s; s = tab; tab [7] = ‘a’; printf (“tab [7] = %c\n”, tab[7]); *(s+7) = ‘b’; printf (“tab [7] = %c\n”, tab[7]); Vediamo l’esempio precedente più da vicino. char tab [100]; char *s; s = tab; /* s e tab sono due sinonimi, con la differenza che s è una variabile puntatore a carattere, mentre tab è una costante */ tab [7] = ‘a’; printf (“tab [7] = %c\n”, tab[7]); *(s+7) = ‘b’; /* incrementando di 7 il valore di s si ottiene un valore corrispondente all’indirizzo dell’ottavo elemento di tab, al quale si accede per mezzo dell’operatore di dereferenziazione. Dunque, si può far riferimento a tab[7] scrivendo *(s+7). */ printf (“tab [7] = %c\n”, tab[7]); Ancora sull’esempio precedente. char tab [100]; char *s; s = tab; tab [7] = ‘a’; printf (“tab [7] = %c\n”, tab[7]); *(s+7) = ‘b’; printf (“tab [7] = %c\n”, tab[7]); tab = tab[0] = ? ... tab[99] = ? s=? tab = tab[0] = ? ... tab[7] = ‘a’ ... tab[99] = ? s= ... tab[7] = ‘b’ ... Inizializzazione di un array Vediamo come si può inizializzare un array in due modi diversi. Primo modo Secondo modo char tab [100]; int i; char tab [100]; char * s; int i; for (i=0; i < 100; i++) tab[i] = ‘k’; s = tab; for (i=0; i < 100; i++) *s++ = ‘k’; L’istruzione *s++ = ‘k’; opera nel seguente modo: ) copia k nella locazione di memoria puntata da s; ) poi incrementa s di un elemento. Attenzione Se per errore avessimo scritto: char tab [100]; char * s; int i; s = tab; for (i=0; i < 300; i++) *s++ = ‘k’; /* 300 invece di 100 */ il compilatore non avrebbe segnalato alcun errore, ma avremmo avuto problemi in esecuzione perché si sarebbe inizializzata con ‘k’ una regione di memoria al di là del limite allocato con tab. Aritmetica dei puntatori Gli operatori ammessi per una variabile di tipo puntatore sono: + ++ - -- Ma qual’è l’esatto significato dell’incremento o decremento di un puntatore? I puntatori non vengono incrementati (o decrementati) come una qualsiasi altra costante numerica. Per esempio, se pc vale 10, dove pc è stato dichiarato: char *pc; non è detto che pc++ valga 11 ! Aritmetica dei puntatori Nell’aritmetica dei puntatori quello che conta è il tipo base. Incrementare di 1 un puntatore significa far saltare il puntatore alla prossima locazione corrispondente a un elemento di memoria il cui tipo coincide con quello base. Esempio int a[10]; char b[10]; int *pi; char *pc; pi = a; pc = b; pi = pi +3; /* sposta in avanti pi di tre posizioni, dove ogni posizione occupa lo spazio di un int */ pc = pc + 3; /* sposta in avanti pc di tre posizioni, dove ogni posizione occupa lo spazio di un char */ Aritmetica dei puntatori Più in generale si ha che, quando un operatore aritmetico è applicato ad un puntatore p di un certo tipo e p punta ad un elemento di un array di oggetti di quel tipo, p+1 significa “prossimo elemento dell’array” mentre p-1 significa “elemento precedente”. ¾ La somma tra due puntatori non è definita. ¾ La sottrazione tra puntatori è definita e ha significato quando entrambi i puntatori puntano ad un elemento dello stesso array. ¾ La sottrazione di un puntatore da un altro produce un numero intero corrispondente al numero di posizioni tra i due elementi dell’array. ¾ Sommando o sottraendo un intero da un puntatore si ottiene ancora un puntatore. Esercizio Dire cosa stampa la seguente porzione di codice. char v[4] = {‘m’, ‘e’, ‘l’, ‘a’}; char *p; p = v; v++; printf(“%d\n”, *p); Soluzione Dire cosa stampa la seguente porzione di codice. char v[4] = {‘m’, ‘e’, ‘l’, ‘a’}; char *p; p = v; v++; /* ERRORE: il valore di v è una costante e non può essere cambiato */ printf(“%d\n”, *p); Esercizio Dire cosa stampa la seguente porzione di codice. char v[4] = {‘m’, ‘e’, ‘l’, ‘a’}; char *p; p = v; p++; p +=v; printf(“%d\n”, *p); Soluzione char v[4] = {‘m’, ‘e’, ‘l’, ‘a’}; char *p; p = v; p++; p +=v; /* ok, questo si può fare */ /* equivale a p = p+v; */ /* ERRORE : non si possono sommare due puntatori */ printf(“%d\n”, *p); Riservare un posto aereo Scriviamo un programma che gestisce le prenotazioni dei posti su un aereo. Rappresentiamo l’aereo come un array i cui elementi corrispondono ai posti disponibili: 1 1 0 0 0 0 0 0 0 0 0 1 MAX_FUMATORI ¾ Un elemento ha valore 0 se corrisponde ad un posto libero, ha valore 1 se corrisponde ad un posto prenotato ¾ Il passeggero può scegliere un posto per fumatori o non fumatori ¾ Esiste un numero massimo di posti che possono essere occupati da fumatori: MAX_FUMATORI Riservare un posto aereo ¾ Inseriamo le prenotazioni per i passeggeri non fumatori partendo dalla testa dell’aereo. ¾ Inseriamo le prenotazioni per i passeggeri fumatori partendo dalla coda, controllando di rimanere sotto il limite massimo. ¾ Teniamo traccia in ogni istante del prossimo posto disponibile per un passeggero fumatore MAX_FUMATORI 1 1 0 0 limite_fumatori 0 0 0 0 0 0 0 1 Riservare un posto aereo #include<stdio.h> #define MAX_CAPIENZA 400 #define MAX_FUMATORI 50 #define TRUE 1 #define FALSE 0 int aereo[MAX_CAPIENZA]={ 0 }; /* l’aereo è una variabile globale inizialmente l’aereo è vuoto */ int prenota_non_fuma( ); /* prenota un posto non fumatori, ritorna TRUE se il posto c’è, ritorna FALSE se non ci sono posti liberi */ int prenota_fuma( int *); /* prende un puntatore al prossimo posto per fumatori. Se riesce a prenotarlo ritorna TRUE, altrimenti ritorna FALSE */ int main( ){ int *limite_fumatori=aereo; int prenotazioni=0; char fuma; int *i; do{ /* punta al primo posto libero per fumatori che inizialmente è la coda dell’aereo */ printf(“Desidera un posto fumatori? (y / n / -)”); scanf(“ %c”, &fuma); switch(fuma) { case ‘n’: if ( prenota_non_fuma() ) prenotazioni++; else printf(“Posti non fumatori terminati\n”); break; case ‘y’: if ( prenota_fuma(limite_fumatori) ) { prenotazioni++; limite_fumatori++; } else printf(“Posti fumatori terminati\n”); break; } } while (prenotazioni < MAX_CAPIENZA && fuma!= ‘-’ ); /* Le prenotazioni sono terminate. Visualizziamo la situazione dell’aereo. */ /* Scorri tutti i posti e dove c’è un posto fumatore stampa F, dove c’è un posto non fumatore stampa N, dove c’è un posto libero stampa Esempio: F F F - - - - N N */ printf(“\n\t Situazione dell’aereo: \n\t”); /* scorriamo l’array non per elementi ma per indirizzo dei suoi elementi */ for (i=aereo ; i<limite_fumatori ; i++) printf(“ F”); /* partendo dalla coda stampo i */ /* posti prenotati dai fumatori */ for ( ; i < aereo+MAX_CAPIENZA ; i++) if (*i == 1) printf(“ N”); else printf(“ –”); printf(“\n\n”); } /* scorri la parte restante */ int prenota_non_fuma( ) { int i; for(i=MAX_CAPIENZA-1; i>=0 ; i--) if ( aereo[ i ]==0 ) { aereo[ i ]=1; return TRUE; } return FALSE; } int prenota_fuma( int *limite) { // usiamo gli indici degli array // e l’operatore [ ] // usiamo un puntatore ad un elemento // e l’operatore * if ( *limite == 0 && (limite-aereo) <= MAX_FUMATORI-1 ) { *limite=1; return TRUE; } else return FALSE; } Array multidimensionali Per memorizzare un array multidimensionale si utilizza una variabile di tipo array specificando il numero di componenti per ciascuna delle dimensioni che lo costituiscono. Esempio int array_2_dim [100] [200]; /* dichiarazione di un array bidimensionale */ int array_3_dim [10] [20] [30]; /* dichiarazione di un array tridimensionale */ Stringhe Una stringa è un array di char che termina con il carattere ‘\0’. Il carattere terminatore ‘\0’ ci permette di trattare le stringhe senza conoscerne a priori la dimensione. In C esistono anche le costanti di stringa che si scrivono tra virgolette, es: “fragola” oppure “via Roma 14” Esempio char frase[ ] = “Analisi, requisiti”; char parola[ ] = {‘m’, ‘a’, ‘r’, ‘e’, ‘\0’}; // inizializzazione con una costante // ci vuole anche il terminatore ‘\0’ Esercizio Dire quali delle seguenti dichiarazioni sono corrette e quali sono errate. 1. char stringa_corta[10]; stringa_corta = “parolina”; 2. char stringa_lunga[ ] = ‘parola molto lunga’; 3. char nome [10] = {G, I, O, I, A, \0}; 4. char nome [10] = “GIOIA”; Soluzione 1. char stringa_corta[10]; stringa_corta = “parolina”; /* errore, stringa_corta è un puntatore il cui valore è costante */ 2. char stringa_lunga[ ] = ‘parola molto lunga’; /* errore: --> “parola molto lunga” */ 3. char nome [10] = {G, I, O, I, A, \0}; /* errore: i caratteri devono essere racchiusi tra apici */ 4. char nome [10] = “GIOIA”; /* corretto */ Array di char e char * Una costante di stringa può essere usata per inizializzare un vettore di caratteri o una variabile di tipo char * 1. char colore[ ] = “verde”; 2. char *p_col = “verde”; La dichiarazione 1. crea un vettore di 6 caratteri: ‘v’, ‘e’, ‘r’, ‘d’, ‘e’, ‘\0’. Una dichiarazione equivalente a 1. è char colore[ ] = {’v’,’e’,’r’,’d’,’e’, ‘\0’}; La dichiarazione 2. crea una variabile che punta alla stringa costante “verde” immagazzinata da qualche parte nella memoria. Quindi: colore[4] = ‘i’; corretto *(p_col + 4) = ‘i’; errore: p_col punta a stringa costante!! Array di stringhe char *colori[4] = { “nero”, “giallo”, “verde”, “blu” }; È un vettore di quattro puntatori a quattro stringhe costanti: ‘n’ ‘e’ ‘r’ ‘o’ ‘\0’ ‘g’ ‘i’ ‘a’ ‘l’ ‘v’ ‘e’ ‘r’ ‘d’ ‘b’ ‘u’ ‘\0’ ‘l’ ‘l’ ‘o’ ‘\0’ ‘e’ ‘\0’ È equivalente ad inizializzare i quattro puntatori separatamente: char *colori[4]; colori[0] = “nero”; colori[1] = “giallo”; colori[2] = “verde”; colori[3] = “blu”; Esercizio Scriviamo una procedura che prende una stringa e la converte nella corrispondente stringa fatta di caratteri tutti maiuscoli. Usiamo un puntatore per scorrere gli elementi. void inMaiuscole(char *s) { while ( *s != ’\0’ ) { if ( *s >= ’a’ && *s <= ’z’ ) *s = *s + (’A’ - ’a’); s++; } } Lettura di una stringa Per leggere (e scrivere) una stringa da input si deve utilizzare la specifica di formato “%s”: char buffer[40]; scanf(“%s”, buffer); 1. Vengono letti da input i caratteri in sequenza fino a trovare il primo carattere di spaziatura (spazio,tabulazione, interlinea, ecc.). 2. I caratteri letti vengono messi dentro il vettore buffer. 3. Al posto del carattere di spaziatura, viene posto il carattere ’\0’. ¾ ¾ il vettore deve essere sufficientemente grande da contenere tutti i caratteri letti più il terminatore ‘\0’. non si usa &buffer ma direttamente buffer perchè buffer è di tipo char*, ovvero è già un indirizzo. Esercizio Scriviamo un programma che legge una data nel formato gg/mm/aaaa e la converte nel formato gg mese aaaa. #include <stdio.h> int main(void) { char *mesi[ ] = { "gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre" }; int mese, giorno, anno; printf("Inserisci una data nel formato gg mm aaaa: "); scanf("%d %d %d", &giorno, &mese, &anno); if (1 <= mese && mese <= 12) printf("La data e’: %d %s %d\n", giorno, mesi[mese-1], anno); else printf("Il mese non e’ corretto.\n"); return 0; } Crittografia Scriviamo un programma per criptare dei messaggi con il metodo di sostituzione Decidiamo un codice di crittografia: a ciascuna lettera associamo un preciso codice, ad esempio a b c d e f … j k 5 l t w … y p z 9 Scriviamo un programma che legge in input un messaggio di 5 parole e lo stampa a video criptato. Per semplicità usiamo solo lettere minuscole #include<stdio.h> void encrypt (char parola[ ], char codice[ ]); /* funzione che cripta una parola usando il codice passato come secondo parametro */ int main( ) { int i; char conversione[26]= { ‘j’, ‘k’, ‘5’, ‘l’, ‘t’,’w’, … ’p’,’9’ }; char frase[5][20]; // codice // conterrà 5 parole lunghe al più 19 caratteri printf(“Inserisci una frase di 5 parole: ”); for(i=0; i<5; i++) { scanf(“%s”, frase[i]); // non serve & poiché frase[i] è un indirizzo encrypt(frase[i], conversione); } printf(“La frase criptata e’ “); for(i=0; i<5; i++) printf (“%s ”, frase[i]); printf(“\n”); } /* fine del main */ /* prende una parola e la cripta sostituendo una lettera per volta */ void encrypt (char parola[ ], char codice[ ]) { int i; for ( i=0 ; parola[i] !=‘\0’ ; i++ ) parola[i] = codice[ parola[i] – ‘a’]; return; } ESERCIZIO: Numeri Primi Si scriva una funzione C che calcola se un numero N è primo, utilizzando la seguente specifica definita ricorsivamente: - 2 è primo; -un numero N è primo se non è divisibile per alcun numero primo compreso fra 2 ed N/2. SOLUZIONE: int div(N,M) { return (N % M) == 0; } int primo(int N) { int i, Trovato = 1; if (N == 2) return 1 else { for (i=2; i<=N/2; i++) if (primo(i)) Trovato = Trovato && !div(N,i); return Trovato; } } Esercizio Si realizzi un programma C che legga da standard input i dati relativi ai corsi tenuti presso una università. In particolare, per ogni corso vengono dati: - denominazione del corso: una stringa che riporta il nome del corso; - nome del docente: una stringa che rappresenta il nome del docente del corso; - cognome del docente: una stringa che rappresenta il cognome del docente del corso; - iscritti: un intero che indica il numero di studenti che frequentano il corso. Il programma deve stampare la denominazione ed il cognome relativi a tutti i corsi che hanno il numero di iscritti maggiore o uguale alla media aritmetica degli iscritti (calcolata su tutti i corsi). Soluzione #include <stdio.h> #define N 300 struct stud { char denominazione[20]; char nome_docente[10]; char cognome_docente[15]; int studenti; } corsi[N]; main() { int i, somma, nc; float media; printf("Inserisci il numero dei corsi "); scanf("%d", &nc); /* inserimento dati */ for (i=0; i<nc; i++) { printf("Inserisci il nome del corso "); scanf("%s",corsi[i].denominazione); printf("Inserisci il nome del docente "); scanf("%s",corsi[i].nome_docente); printf("Inserisci il cognome del docente "); scanf("%s",corsi[i].cognome_docente); printf("Inserisci il numero degli iscritti"); scanf("%d",&corsi[i].studenti); } somma=0; for (i=0; i< nc; i++) somma=somma + corsi[i].studenti; media=((float)somma)/nc; for (i=0; i< nc; i++) if (corsi[i].studenti>=media) printf("%s %s\n",corsi[i].denominazione, corsi[i].cognome_docente); }