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);
}