Lezione 10

Transcript

Lezione 10
Esercitazione 10
Strutture ed Enumerazioni
Allocazione dinamica di memoria
ESERCIZIO
Scrivere una funzione che, dato un array di interi bidimensionale di
dimensione n × n, calcoli e stampi la somma degli elementi che
stanno sotto, sopra e sulla diagonale principale.
0
1
2
3
0
4
0
3
-3
64
0
57
2 2
30
2
23
3 11
78
18
84
0
1
somma degli elementi sotto la diagonale: -3+2+30+11+78+18
somma degli elementi sopra la diagonale: 4+0+3+0+57+23
somma degli elementi sulla diagonale: 0+64+2+84
Funzione somma
*/ la funzione somma utilizza i puntatori per restituire i tre valori richiesti. */
void somma (int a [ MAX_N][MAX_N], int n,
int *somma_sotto, int *somma_sopra, int *somma_diag) {
int i, j;
*/ inizializzazione delle variabili per le somme */
*somma_sotto = 0;
*somma_sopra = 0;
*somma_diag = 0;
*/ somma degli elementi sotto la diagonale principale */
for (i=1; i<n; i++)
for (j =0; j<i ; j++)
*somma_sotto +=a[i][j];
*/ somma degli elementi sopra la diagonale principale */
for (i=0; i<n-1; i++)
for (j =i+1; j<n ; j++)
*somma_sopra +=a[i][j];
*/ somma degli elementi sulla diagonale principale */
for (i=0; i < n; i++)
*somma_diag +=a[i][i];
return ;
}
Typedef
La parola chiave typedef viene usata per assegnare un alias a un
qualsiasi tipo, fondamentale o derivato.
Esempio
typedef char * Stringa;
Stringa s1, s2;
Il tipo char *, cioè il tipo puntatore a carattere, viene ribattezzato
Stringa. Quindi si definiscono due variabili, s1 e s2 di tipo Stringa.
Typedef
typedef int Interi;
// Il tipo int viene ribattezzato Interi
typedef Stringa Lista_spesa[30];
Lista_spesa tab;
tab[0]=“pane”; tab[1]=“latte”;
Lista_spesa è il tipo array di 30 puntatori a carattere.
La variabile tab di tipo Lista_spesa è un array di puntatori a
carattere, di cui inizializzo i prime due.
Tipi derivati
A partire dai tipi fondamentali (int, float, double, char) è possibile
costruire nuovi tipi, detti tipi derivati.
Gli array e i puntatori sono esempi di tipi derivati, nel senso che per
essere specificati hanno bisogno di riferirsi a un tipo base.
Altri esempi di tipi derivati sono
¾
i tipi structure (strutture)
¾
i tipi enumerazione
Tipi structure
Esempio Il seguente tipo struttura rappresenta il concetto di data.
struct Data {
int
giorno;
char *mese;
int
anno;
};
/* dichiarazione del tipo data */
Questa dichiarazione introduce un nuovo tipo, il tipo Data. È ora
possibile dichiarare variabili di tipo struct Data nel seguente modo:
struct Data ieri, oggi;
/* ieri e oggi sono variabili di tipo struct Data */
La variabile oggi è una variabile strutturata composta di tre campi:
due di tipo int - giorno e anno - e una di tipo stringa - mese.
Tipi structure
La sintassi generale per la definizione di un tipo struttura è:
struct nome_struttura {
tipo_campo1 nome_campo1;
tipo_campo2 nome_campo2;
...
tipo_campoN nome_campoN;
};
Gli elementi di una struttura sono detti campi; essi sono identificati da un
nome e da un tipo che può essere sia fondamentale che derivato.
La struttura così definita è un nuovo tipo a tutti gli effetti.
Si possono definire variabili di tipo struct nome_struttura come segue:
struct nome_struttura nome_variabile;
typedef e tipi structure
Esempio
typedef struct {
int
giorno;
char *mese;
int
anno;
} Data ;
/* dichiarazione del tipo Data */
Questa dichiarazione introduce un nuovo nome per il tipo struttura.
È ora possibile dichiarare variabili di tipo Data nel seguente modo:
Data ieri, oggi;
/* ieri e oggi sono variabili di tipo Data */
Esempio
La seguente definizione introduce la struttura Automobile.
Vengono dichiarate quindi due variabili di tipo Automobile, a1 e a2.
typedef struct {
char *marca;
char *modello;
int
numero_vendute;
} Automobile;
Automobile a1, a2;
Tipi structure
Per accedere ai campi di una variabile di tipo struttura si fa uso
dell’operatore punto (.)
ESEMPIO
Si consideri la struttura Data e le variabili ieri e oggi di tipo Data.
Possiamo inizializzare le variabili ieri e oggi nel seguente modo:
oggi.giorno = 30;
oggi.mese = “Novembre”;
oggi.anno = 2007;
ieri.anno = oggi.anno;
printf(“%d %s %d”, oggi.giorno, oggi.mese, oggi.anno);
Operazioni su strutture
Si possono assegnare variabili di tipo struttura a variabili dello stesso
tipo struttura.
Data d1, d2;
...
d1 = d2;
Nota: questo permette di assegnare interi vettori.
typedef struct {
int a[12];
char b; } Prova x, y;
...
x = y;
Non è possibile effettuare il confronto tra due variabili di tipo struttura.
Data d1, d2;
if (d1 == d2) ... Errore!
Esempio
/* definizione della struttura Automobile */
#define <stdio.h>
typedef struct {
char *marca;
char *modello;
int numero_vendute;
} Automobile;
int main ( )
{
Automobile a1, a2;
(continua)
a1.marca = “FERRARI”;
a1.modello = “F40”;
a1.numero_vendute = 200;
a2.marca = “OPEL”;
a2.modello = “ASTRA”;
a2.numero_vendute = 2000;
printf (“marca auto = %s\n”, a1.marca);
printf (“modello auto = %s\n”, a1.modello);
printf (“vendute = %d\n”, a1.numero_vendute);
printf (“marca auto = %s\n”, a2.marca);
printf (“modello auto = %s\n”, a2.modello);
printf (“vendute = %d\n”, a2.numero_vendute);
}
ESERCIZIO
Stabilire se il seguente codice e’ sintatticamente corretto e, in tal caso,
cosa produce a video.
typedef struct {
int a;
} S1;
typedef struct {
int a;
} S2;
S1 bob, ric;
S2 gio;
bob.a = 30;
ric = bob;
gio = bob;
printf (“%d, %d, %d \n”, bob.a, ric.a, gio.a);
SOLUZIONE
typedef struct {
int a;
} S1;
typedf struct {
int a;
} S2;
S1 bob, ric;
S2 gio;
bob.a = 30;
ric = bob;
gio = bob;
/* errore: non si può assegnare una variabile di tipo
S1 a una di tipo S2 */
printf (“%d, %d, %d \n”, bob.a, ric.a, gio.a);
ESERCIZIO
Stabilire se il seguente codice e’ sintatticamente corretto e, in tal caso,
cosa produce a video.
typedef struct {
int
giorno;
char *mese;
int
} Data;
anno;
Data *pd, compleanno;
pd = &compleanno;
*pd.giorno = 30;
*pd.mese = “Novembre”;
*pd.anno = 2007;
printf (“Il mio compleanno e’ il: %d, %s, %d \n”, compleanno.giorno,
compleanno.mese, compleanno.anno);
SOLUZIONE
typedf struct {
int
giorno;
char *mese;
int
} Data;
anno;
Data *pd, compleanno;
pd = &compleanno;
*pd.giorno = 30;
*pd.mese = “Novembre”;
*pd.anno = 2007;
/* errore: poiché l’operatore “.” ha priorità
maggiore rispetto all’operatore “*” bisogna
usare le parentesi: (*pd).giorno ... ,
(*pd).mese..., (*pd).anno ... */
printf (“Il mio compleanno e’ il: %d, %s, %d \n”, compleanno.giorno,
compleanno.mese, compleanno.anno);
ESERCIZIO
Stabilire se il seguente codice e’ sintatticamente corretto e, in tal caso, cosa
produce a video.
typedef struct {
int
giorno;
char *mese;
int
} Data;
anno;
Data compleanno = { 30, “Novembre”, 2007 };
Data *pd;
pd = &compleanno;
pd -> giorno = compleanno -> giorno;
pd -> mese = compleanno -> mese;
pd -> anno = compleanno -> anno;
printf (“Il mio compleanno : %d, %s, %d \n”, pd.giorno, pd.mese, pd.anno);
SOLUZIONE
typedef struct {
int
giorno;
char *mese;
int
} Data;
anno;
Data compleanno = { 30, “Novembre”, 2007 };
Data *pd;
pd = &compleanno;
pd -> giorno = compleanno -> giorno;
pd -> mese = compleanno -> mese;
pd -> anno = compleanno -> anno;
/ / errore: compleanno.giorno
/ / errore: compleanno.mese
/ / errore: compleanno.anno
printf (“Il mio compleanno : %d, %s, %d \n”, pd.giorno, pd.mese, pd.anno);
/ / errore: pd->giorno, pd->mese, pd->anno
Esercizio
Scrivere una funzione che, dato un puntatore a una variabile di tipo
Data, restituisce
¾
¾
il numero del mese relativo alla data puntata dal puntatore
in caso di errore, restituisce 0.
SOLUZIONE
int numero_mese (Data *pd)
{
if (! strcmp(pd -> mese , “Gennaio”))
/* l’operatore “ -> ” permette
return (1);
di accedere direttamente ai campi di una
variabile strutturata puntata da un puntatore */
if (! strcmp (pd -> mese , “Febbraio”))
return (2);
....
if (! strcmp (pd -> mese , “Dicembre”))
return (12);
return (0); }
Strutture di Strutture
In C è possibile definire strutture di strutture
Regola: le strutture che compaiono nei campi di una struttura
devono essere state definite prima della struttura che
le contiene.
Esempio
Definire una struttura utile per la gestione di un autosalone.
typedef struct {
int
giorno;
char *mese;
int
} Data;
anno;
typedef struct {
char *marca;
char *modello;
int
numero_vendute;
Data data_prima_produzione;
} Automobile;
Automobile salone[100];
Esempio
Definire una struttura utile per la gestione di un conto corrente.
typedef struct {
int
giorno;
char *mese;
int
} Data;
anno;
typedef struct {
int
numero_conto;
char nome[80];
float bilancio;
Data ultimo_movimento;
} Conto_corrente;
Tipi enumerazione
Spesso si ha l’esigenza di definire un insieme finito di valori alternativi
da associare ad un oggetto. Ad esempio i 4 semi delle carte da poker
sono “cuori”, “fiori”, “quadri”, “picche”.
In genere si memorizzano questi valori associando un numero costante
univoco a ciascuno. Perciò si potrebbe scrivere
const int cuori = 1;
const int fiori = 2;
const int quadri = 3;
const int picche = 4;
Questa tecnica ha dei punti deboli. Il principale è che non c’e’ modo
di restringere l’insieme dei valori assegnabili ad una variabile ai soli
“cuori”, “fiori”, “quadri”, “picche”.
Tipi enumerazione
I tipi enumerazione forniscono un metodo alternativo non solo per
definire ma anche per raggruppare insiemi di costanti.
Ad esempio:
enum Seme {cuori = 1, fiori, quadri, picche} ;
Così ho dichiarato un nuovo tipo enum Seme, che è un tipo enumerazione.
I valori che appartengono al tipo enum Seme sono solo quattro: cuori, fiori,
quadri, picche, e corrispondono agli interi 1,2,3 e 4.
Nota: per default, al primo enumeratore è assegnato il valore 0 e ai
successivi i valori 1,2,ecc...
Noi abbiamo assegnato a cuori il valore 1. A fiori è assegnato
automaticamente il valore 2, a quadri il valore 3 e a picche il valore 4.
typedef e tipi enumerazione
Esempio:
typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme ;
Seme p, s;
Così ho rinominato il tipo enum Seme con il nuovo nome Seme.
p ed s sono due variabili di tipo Seme.
Tipi enumerazione
Oggetti di tipo enumerazione possono essere definiti, prendere parte a
espressioni ed essere passati come argomenti a funzioni.
Un oggetto di tipo enumerazione può essere inizializzato con - oppure è
possibile assegnargli - solo un oggetto dello stesso tipo enumerazione.
Esempio
typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;
Seme s = quadri;
Seme p = 4;
s = p;
/* corretto, corrisponde a s = 3 */
/* corretto (ma “brutto”), equivale a p = picche */
/* corretto */
Tipi enumerazione
Non è possibile stampare i nomi effettivi degli enumeratori.
Esempio
typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;
Seme s = quadri;
printf(“s = %d”, s);
/* stampa s = 3 */
Esercizio
Dire cosa stampa il seguente frammento di codice.
typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;
typed struct {
Seme seme;
int
numero;
} Carta;
Carta carta1, carta2;
carta1.seme = cuori;
carta1.numero = 1;
carta2.seme = quadri;
carta2.numero = 10;
carta1 = carta2;
printf(“seme carta 1 = %d\n”, carta1.seme);
printf(“numero carta 1 = %d\n”, carta1.numero);
Soluzione
typedef enum Seme {cuori = 1, fiori, quadri, picche} Seme;
typedef struct {
Seme seme;
int
numero;
} Carta;
stampa
Carta carta1, carta2;
carta1.seme = cuori;
seme carta 1 = 3
carta1.numero = 1;
numero carta 1 = 10
carta2.seme = quadri;
carta2.numero = 10;
carta1 = carta2;
printf(“seme carta 1 = %d\n”, carta1.seme);
printf(“numero carta 1 = %d\n”, carta1.numero);
Esercizio
¾
Consideriamo il seguente tipo di dato:
typedef struct Student{
char cognome[15];
char nome[15]
int voto;
} Studente;
¾
Vogliamo costruire delle procedure che ci permettano di
lavorare con tabelle di studenti: riempirle (con dati immessi
dall’utente), ordinarle in ordine alfabetico, stamparle, ecc.
void leggi(Studente classe[], int nstudenti){
int i;
for ( i = 0 ; i < nstudenti ; i++ ){
printf("\ncognome = ");
Viene passato
scanf("%s", classe[i].cognome);
il riferimento
printf("nome = ");
all’array
scanf("%s", classe[i].nome);
(nessuna copia!)
printf("voto = ");
scanf("%d", &classe[i].voto) ;
}
}
void scrivi(Studente classe[], int nstudenti){
int i;
for ( i = 0 ; i < nstudenti ; i++ ){
printf("\n cognome = %s“ classe[i].cognome);
printf("\n nome
= %s", classe[i].nome);
printf("\n voto
= %d\n", classe[i].voto);
}
}
Ordinamento per inserzione
… passi successivi
0
1
2
4
6
i-1
i
9
5
Parte già ordinata
3
8
1
7
Elemento da
considerare
adesso
0
Ordinamento per inserzione
… passi successivi
0
1
2
4
temp
i-1
6
5
9
i
3
8
1
Elemento da
considerare
adesso
7
0
Ordinamento per inserzione
… passi successivi
0
1
i-1
2
4
6
temp
5
i
9
3
8
1
7
0
Ordinamento per inserzione
… passi successivi
0
1
i-1
i
2
4
6
9
temp
5
3
8
1
7
0
Ordinamento per inserzione
… passi successivi
0
1
2
4
5
i-1
i
6
9
Parte già ordinata
temp
3
8
1
7
0
Prossimo elemento
da considerare
Torniamo all’esercizio...
¾
La tecnica di ordinamento per inserzione si applica ad array di
qualsiasi tipo.
¾
In questo caso particolare, due elementi stud1 e stud2 dell’array
sono ordinati se
stud1.cognome < stud2.cognome
oppure
stud1.cognome == stud2.cognome e
stud1.nome < stud2.nome
rispetto all’ordine lessicografico (quello dell’elenco telefonico!)
Ordinamento
int maggiore(Studente p, Studente q){
return (strcmp(p.cognome, q.cognome)>0 ||
(strcmp(p.cognome, q.cognome)==0
&& strcmp(p.nome, q.nome)>0));
}
void inserisci( Studente a[], int i ){
int h;
Studente temp = a[i] ;
}
for ( h = i ; h > 0 && maggiore(a[h-1],temp); h-- ){
a[h] = a[h-1];
}
a[h] = temp ;
void ordina(Studente a[], int nstudenti){
int i;
for (i=1; i<nstudenti; i++)
inserisci(a,i);
}
Fine esercizio...
¾
A questo punto non rimane che scrivere il programma
principale...
#include<stdio.h>
#include <string.h>
#define NUM_STUDENTI 300
...
int main(void){
Studente classe[NUM_STUDENTI];
int nstud = 120;
leggi(classe, nstud);
scrivi(classe, nstud);
ordina(classe, nstud);
scrivi(classe,nstud);
}
Esercizio
Dichiarare un tipo enumerazione Colore che rappresenta i due colori
delle pedine degli scacchi (bianco e nero), ed un tipo enumerazione
Personaggio che rappresenta i diversi tipi di pedina (re, regina, alfiere,
cavallo, torre e pedone).
typedef enum Colore { bianco, nero } Colore;
typedef enum Personaggio { re, regina, alfiere, cavallo
torre, pedone } Personaggio;
Esercizio
Dichiarare un tipo record Pedina, che rappresenta una pedina degli
scacchi, con due campi: colore e personaggio, di tipo Colore e
Personaggio, respettivamente.
typedef struct {
Colore
colore;
Personaggio personaggio;
} Pedina;
Esercizio
Dichiarare un tipo record Casella, che rappresenta una casella della
scacchiera, ed è un record di due campi: colore (il colore della casella,
di tipo Colore) e pezzo, un riferimento ad una variabile di tipo Pedina (il
riferimento sarà NULL se la casella non contiene nessuna pedina,
altrimenti conterrà l’indirizzo di una variabile di tipo Pedina).
typedef struct {
Colore colore;
Pedina *pezzo;
} Casella;
Esercizio
Dichiarare il tipo Scacchiera, i cui elementi sono tabelle bidimensionali
di dimensione 8 per 8 aventi come tipo base il tipo Casella.
Un valore di tipo Scacchiera rappresenta una possibile configurazione
della scacchiera.
typedef Casella Scacchiera [8][8];
Scacchiera s;
Esercizio
Utilizzando i tipi definiti precedentemente, inizializzare una variabile s di
tipo Scacchiera in modo che rappresenti una scacchiera in cui tutte le
caselle sono nere e sia presente solo il re bianco in posizione (0,0).
Scacchiera s;
int i, j;
Pedina *king;
for (i=0; i<8; i++)
for (j=0; j<8; j++) {
s[ i ][ j ].colore = nero;
s[ i ][ j ].pezzo = NULL; }
king = (Pedina*) malloc(sizeof(Pedina));
king ->colore = bianco;
king ->personaggio = re;
s[0][0].pezzo = king;
Esercizio
Utilizzando il modello ambiente-memoria, descrivere graficamente la
situazione al termine dei comandi necessari a realizzare quanto
richiesto dall’esercizio precedente.
memoria
ambiente
Scacchiera s
Pedina *king
stack
heap
nero
•
nero
NULL
nero
NULL
nero
NULL
....
•
bianco
re
Esercizio
Utilizzando i tipi definiti negli esercizi precedenti, scrivere la definizione
di una funzione che,
¾
prendendo come parametro attuale un valore s di tipo Scacchiera
verifica se la torre nera è presente nella scacchiera s.
La funzione restituisce 1 se la torre nera è presente, altrimenti
restituisce 0.
Soluzione
int cercaTorreNera (Scacchiera s) {
int i, j;
for (i=0; i<8; i++)
for (j=0; j<8; j++)
if ((s[i][j].pezzo != NULL) &&
((s[i][j].pezzo)->colore == nero) &&
((s[i][j].pezzo)->personaggio == torre) )
return 1;
return 0;
}
Allocazione dinamica
typedef struct {
char titolo[30];
char autore[15];
} Libro;
Libro *lib;
lib = (Libro *) malloc (sizeof(Libro));
printf(“Inserisci titolo senza spazi: “);
scanf(“%s”, lib -> titolo);
printf(“Inserisci autore: “);
scanf(“%s”, lib -> autore);
Libro
titolo
autore
Allocazione dinamica
typedef struct {
char titolo[30];
char autore[15];
} Libro;
Libro
titolo
/* altro modo di inizializzare */
Libro *lib;
autore
lib = (Libro *) malloc (sizeof(Libro));
strcpy( lib -> titolo, “La divina commedia”);
strcpy( lib -> autore, “Dante”);
NOTA:
Se inserisco “Harry Potter e l’ordine
della fenice”, si esce dai limiti !!!
E se inserisco “Iliade” spreco spazio
Allocazione dinamica
typedef struct {
char *titolo;
char *autore;
} Libro;
Libro *lib;
Libro
lib = (Libro *) malloc (sizeof(Libro));
titolo
/* così ho allocato spazio solo per due puntatori */
/* prima di inserire le stringhe per titolo e autore
occorre allocare lo spazio necessario a
contenerle
*/
autore
Allocazione dinamica
typedef struct {
char *titolo;
char *autore;
Libro
} Libro;
titolo
Libro *lib;
lib = (Libro *) malloc (sizeof(Libro));
lib->titolo =(char *) malloc (sizeof(char)*40);
autore
lib->autore =(char *) malloc (sizeof(char)*15);
strcpy( lib->titolo, “Harry Potter e l’ordine della fenice”);
strcpy( lib->autore, “J.K. Rowling”);
Allocazione dinamica
/* così posso usare la memoria in modo efficiente: */
Libro *lib2;
lib2 = (Libro *) malloc (sizeof(Libro));
lib2->titolo =(char *) malloc (sizeof(char)*7);
lib2->autore =(char *) malloc (sizeof(char)*6);
strcpy( lib2->titolo, “Iliade”);
strcpy( lib2->autore, “Omero”);
Libro
titolo
‘I’ ‘l’ ‘i’ ‘a’ ‘d’ ‘e’ ‘\0’
‘O’ ‘m’ ‘e’ ‘r’ ‘o’ ‘\0’
autore