Strutture ed Enumerazioni Allocazione dinamica di memoriaprog1/Esercizi_in_aula/Esercizi10.pdf ·...
Transcript of Strutture ed Enumerazioni Allocazione dinamica di memoriaprog1/Esercizi_in_aula/Esercizi10.pdf ·...
ESERCIZIOScrivere una funzione che, dato un array di interi bidimensionale di
dimensione n × n, calcoli e stampi la somma degli elementi chestanno sotto, sopra e sulla diagonale principale.
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
0 4 0 3
-3 64 0 57
2 30 2 23
11 78 18 84
0
1
2
3
0 1 2 3
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 ;
}
TypedefLa 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.
Typedeftypedef 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) è possibilecostruire nuovi tipi, detti tipi derivati.
Gli array e i puntatori sono esempi di tipi derivati, nel senso che peressere specificati hanno bisogno di riferirsi a un tipo base.
Altri esempi di tipi derivati sono
i tipi structure (strutture)i tipi enumerazione
Tipi structureEsempio 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 structureLa 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 structureEsempio
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 */
EsempioLa 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 usodell’operatore punto (.)
ESEMPIOSi 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 struttureSi 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);
}
ESERCIZIOStabilire 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);
ESERCIZIOStabilire se il seguente codice e’ sintatticamente corretto e, in tal caso,
cosa produce a video.
typedef struct {int giorno;char *mese;int anno;
} Data;
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 anno;
} Data;
Data *pd, compleanno;pd = &compleanno;*pd.giorno = 30; /* errore: poiché l’operatore “.” ha priorità*pd.mese = “Novembre”; maggiore rispetto all’operatore “*” bisogna*pd.anno = 2007; 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);
ESERCIZIOStabilire se il seguente codice e’ sintatticamente corretto e, in tal caso, cosa
produce a video.
typedef struct {int giorno;char *mese;int anno;
} Data;
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 anno;
} Data;
Data compleanno = { 30, “Novembre”, 2007 };
Data *pd;pd = &compleanno;pd -> giorno = compleanno -> giorno; / / errore: compleanno.giornopd -> mese = compleanno -> mese; / / errore: compleanno.mesepd -> anno = compleanno -> anno; / / 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 tipoData, restituisce
il numero del mese relativo alla data puntata dal puntatorein 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.
EsempioDefinire una struttura utile per la gestione di un autosalone.
typedef struct {int giorno;char *mese;int anno;
} Data;
typedef struct {char *marca;char *modello;int numero_vendute;Data data_prima_produzione;
} Automobile;
Automobile salone[100];
EsempioDefinire una struttura utile per la gestione di un conto corrente.
typedef struct {int giorno;char *mese;int anno;
} Data;
typedef struct {int numero_conto;char nome[80];float bilancio;Data ultimo_movimento;
} Conto_corrente;
Tipi enumerazioneSpesso 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’ mododi restringere l’insieme dei valori assegnabili ad una variabile ai soli“cuori”, “fiori”, “quadri”, “picche”.
Tipi enumerazioneI 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 enumerazioneEsempio:
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 enumerazioneOggetti 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; /* corretto, corrisponde a s = 3 */Seme p = 4; /* corretto (ma “brutto”), equivale a p = picche */s = p; /* corretto */
Tipi enumerazioneNon è 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 */
EsercizioDire 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;
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);
stampa
seme carta 1 = 3numero carta 1 = 10
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.
Esercizio
void leggi(Studente classe[], int nstudenti){int i;for ( i = 0 ; i < nstudenti ; i++ ){
printf("\ncognome = ");scanf("%s", classe[i].cognome);printf("nome = ");scanf("%s", classe[i].nome);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);
} }
Viene passato il riferimento
all’array(nessuna copia!)
Ordinamento per inserzione
2 4 6 9 5 3 8 1 7
Parte già ordinata Elemento da considerare
adesso
0
… passi successivi0 1 ii-1
Ordinamento per inserzione
2 4 6 9 3 8 1 7
Elemento da considerare
adesso
0
… passi successivi0 1 ii-1
5temp
Ordinamento per inserzione
2 4 6 9 3 8 1 7 0
… passi successivi0 1 ii-1
5
temp
Parte già ordinataProssimo 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’arraysono ordinati se
stud1.cognome < stud2.cognomeoppure
stud1.cognome == stud2.cognome e stud1.nome < stud2.nome
rispetto all’ordine lessicografico (quello dell’elenco telefonico!)
Ordinamentoint 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);
}
EsercizioDichiarare 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, cavallotorre, pedone } Personaggio;
EsercizioDichiarare 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;
EsercizioDichiarare 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;
EsercizioDichiarare 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;
EsercizioUtilizzando 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;
EsercizioUtilizzando il modello ambiente-memoria, descrivere graficamente la situazione al termine dei comandi necessari a realizzare quanto richiesto dall’esercizio precedente.
Scacchiera s heapstackmemoriaambiente
NULL
biancore
nero
nero
nero
nero
NULL
NULL
....
•Pedina *king
•
Esercizio
Utilizzando i tipi definiti negli esercizi precedenti, scrivere la definizione di una funzione che,
prendendo come parametro attuale un valore s di tipo Scacchieraverifica 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 dinamicatypedef 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);
Librotitolo
autore
Allocazione dinamicatypedef struct {
char titolo[30];char autore[15];
} Libro;
/* altro modo di inizializzare */Libro *lib;
lib = (Libro *) malloc (sizeof(Libro));
strcpy( lib -> titolo, “La divina commedia”);strcpy( lib -> autore, “Dante”);
Librotitolo
autore
NOTA:Se inserisco “Harry Potter e l’ordine della fenice”, si esce dai limiti !!!
E se inserisco “Iliade” spreco spazio
Allocazione dinamicatypedef struct {
char *titolo;char *autore;
} Libro;
Libro *lib;
lib = (Libro *) malloc (sizeof(Libro));
/* così ho allocato spazio solo per due puntatori */
/* prima di inserire le stringhe per titolo e autore
occorre allocare lo spazio necessario a
contenerle */
Libro
titolo
autore
Allocazione dinamicatypedef struct {
char *titolo;char *autore;
} Libro;
Libro *lib;
lib = (Libro *) malloc (sizeof(Libro));
lib->titolo =(char *) malloc (sizeof(char)*40);
lib->autore =(char *) malloc (sizeof(char)*15);
strcpy( lib->titolo, “Harry Potter e l’ordine della fenice”);strcpy( lib->autore, “J.K. Rowling”);
Libro
titolo
autore
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”);
Librotitolo
autore
‘I’ ‘l’ ‘i’ ‘a’ ‘d’ ‘e’ ‘\0’
‘O’ ‘m’ ‘e’ ‘r’ ‘o’ ‘\0’