160 likes | 307 Vues
Capitolo 10 (Deitel) Strutture, unioni ed enumerazioni. Sommario 10.1 - Introduzione 10.2 - Definire le strutture 10.3 - Dichiarare e inizializzare le strutture 10.4 - Accedere ai membri delle strutture 10.5 - Usare le strutture con le funzioni 10.6 - Ridenominazione di tipi con typedef
E N D
Capitolo 10 (Deitel)Strutture, unioni ed enumerazioni Sommario 10.1 - Introduzione 10.2 - Definire le strutture 10.3 - Dichiarare e inizializzare le strutture 10.4 - Accedere ai membri delle strutture 10.5 - Usare le strutture con le funzioni 10.6 - Ridenominazione di tipi con typedef 10.7 - Caso di studio: un efficiente mescolatore/distributore di carte 10.8 - Le Unioni 10.9 - Le costanti di Enumerazione
10.1 - Introduzione • I vettori • Permettono di memorizzare una serie di dati omogenei in tipo • Si può accedere ad ogni singolo dato specificandone l’indice/posizione • Ma come memorizzare una serie di dati non necessariamente omogenei? • Ad esempio, come memorizzare i dati di una persona (nome, indirizzo, età, ..)? • Si deve davvero dichiarare una variabile per ogni tipo di dato della persona e duplicarla N volte per il numero di persone da memorizzare? • Le strutture • Sono raggruppamenti di variabili correlate (aggregati) sotto un unico nome • Tale nome indica il “tipo di struttura”, che è definito dal programmatore • Possono contenere variabili eterogenee in tipo (in genere tra quelli base del C) • Numero/tipo di variabili che definiscono una struttura è dato dal programmatore • Sono come vettori senza vincolo di avere elementi tutti dello stesso tipo e i cui elementi non sono identificati da un indice, ma da un nome • Sono usate comunemente per definire i record da salvare nei file • Se combinate con i puntatori, permettono di creare liste concatenate ed altre strutture dati dinamiche complesse (come pile, code e alberi)
10.2 - Definire le strutture (1/2) • Sintassi struct nome_struttura{ tipo_base1 nome_membro1; tipo_base2 nome_membro2; ... }; • La parola chiave per definire una struttura dati è struct, seguita dal nome della struttura, che sarà poi il tipo delle variabili/istanze di tale struttura • La definizione della struttura descrive il modello dati che essa implementa, costituito da un insieme di variabili dette membri della struttura • Esempio: struct carta{ char *figura; char *segno; }; • struct introduce la definizione della struttura carta • carta è il nome della struttura che verrà poi usato per dichiarare variabili aventi questo tipo di struttura • carta contiene due membri di tipo char * (figura e segno)
10.2 - Definire le strutture (2/2) • Regole e vincoli inerenti le strutture • La definizione dichiara un tipo di dato ma non alloca spazio in memoria • Una struttura non può avere come membri variabili che hanno come tipo la struttura stessa, ma può tuttavia contenere puntatori a tali variabili • Ovvero puntatori a variabili che hanno come tipo la struttura stessa • Si possono annidare strutture differenti (come membri di altre) struct nome_struttura1{ tipo_base1 nome_membro1; struct nome_struttura2 nome_membro2; //OK struct nome_struttura1 nome_membro3; //Errore!!! struct nome_struttura1 * nome_membro4; //OK }; • Operazioni valide/permesse • Assegnare variabili struct ad altre variabili aventi lo stesso tipo di struct • Rilevare l’indirizzo di memoria (&) di una variabile/istanza di struttura • Accedere ai valori dei membri/campi di un’istanza di struttura • Determinare la dimensione in memoria della struttura (sizeof)
10.3 - Dichiarare e inizializzare le strutture • Dichiarazione (alloca spazio in memoria) • Una variabile struct si dichiara come tutte le altre struct carta unaCarta, mazzo[ 52 ], *cPtr; • Si può anche unire dichiarazione di variabile e definizione della struct struct carta {//Dichiara una variabile di tipo struct carta, char *figura;//un vettore di elementi di tipo struct carta char *segno;//e un puntatore a variabili di tipo struct carta } unaCarta, mazzo[ 52 ], *cPtr; • Inizializzazione • Puè essere fatta all-in-one tramite una lista di inizializzatori struct carta treCuori = { "Tre", “Cuori" }; • Oppure tramite assegnamento di variabili struct struct carta unaCarta = { "Tre", “Cuori" }; struct carta treCuori = unaCarta; • Oppure separando dichiarazione e inizializzazioni di ogni membro struct carta treCuori; treCuori.figura = “Tre”; treCuori.segno = “Cuori”;
10.4 - Accedere ai membri delle strutture • Accesso diretto • Per accedere al valore dei membri delle strutture (istanze) in modo diretto tramite la variabile/istanza stessa, si usa l’operatore punto (.) • L’operatore punto va applicato alla variabile struct e deve essere seguito dal nome del membro di cui si vuole reperire il valore struct carta miaCarta; printf( "%s", miaCarta.segno ); • Accesso via puntatore • Per accedere al valore dei membri tramite un puntatore all’istanza di struttura dati, si usa l’operatore freccia (->) • L’operatore freccia va applicato al puntatore alla variabile struct e deve essere seguito dal nome del membro di cui si vuole reperire il valore structcarta *miaCartaPtr = &miaCarta; printf( "%s", miaCartaPtr->segno ); • I due tipi di accesso sono equivalenti • Ovvero miaCartaPtr->segno è alias di (*miaCartaPtr).segno
10.5 - Usare le strutture con le funzioni • Passare le strutture come parametri alle funzioni • Si può passare una struttura interamente oppure solo i singoli membri • In entrambi i casi, il passaggio di default è per valore • Nel primo caso, nel corpo della funzione si accede ai membri usando (.) • Passare le strutture per riferimento • Si deve passare l’indirizzo della variabile struct (&) • La funzione riceve per parametro un puntatore alla variabile struct • Nel corpo della funzione si accede ai membri usando (->) • Con le strutture si può passare un intero vettore per valore • Si crea una struttura dati che ha come unico membro un vettore • Si dichiara una variabile struct che ha per tipo la struttura appena definita • Si passa direttamente la variabile struct per intero alla funzione • In questo modo anche il vettore è automaticamente passato per valore • Viceversa, si può passare una struct per riferimento creando e passando un vettore di struct con un solo elemento, la variabile struct da passare
10.6 - Ridenominazione di tipi con typedef • typedef • Crea sinonimi (alias) per tipi di dato strutturato già definiti in precedenza • In genere si usa typedef per abbreviare i nomi dei certi tipi di struttura • typedef non crea un nuovo tipo di dato, ma solo un alias • Funziona anche con i tipi di dato classici del C (int, float, ..) • Sintassi typedef nome_struttura nome_alias_tipo • Esempio: typedef struct carta Carta; typedef Carta * CartaPtr; • Definisce un nuovo nome di tipo CartaPtr come un sinonimo per il tipo Carta* (che è il tipo “puntatore a struttura dati Carta”) • Da ora per creare puntatori a variabili struct di tipo Carta, si può usare in modo equivalente struct carta * nomePointer oppure CartaPtr nomePointer
1 /* Fig. 10.3: fig10_03.c 2 Il programma per mescolare e distribuire carte usando strutture */ 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <time.h> 6 7 structcarta{ 8 constchar *figura; 9 constchar *segno; 10 }; 11 typedefstructcarta Carta; 12 13 void creaMazzo( Carta * const, constchar *[], constchar *[] ); 14 voidmischiaMazzo( Carta * const ); 15 voiddaiCarte( constCarta * const ); 16 17 int main(){ 18 Carta mazzo[ 52 ]; 19 constchar *figura[] = { "Asso", "Due", "Tre", “Quattro", 20 “Cinque", "Sei", "Sette", “Otto", "Nove", 21 “Dieci", "Jack", “Regina", “Re"}; 22 constchar *segno[] = { “Cuori", “Quadri", “Fiori", “Picche"}; 10.7 - Caso di studio: un efficiente mescolatore/distributore di carte (1/4) 1. Definisce la struttura “carta” composta da due stringhe (come array di caratteri) 2. Rinomina il tipo “carta” in “Carta” 3. Prototipi di funzioni 4. Dichiara il vettore mazzo, i cui elementi sono istanze del tipo strutturato “Carta” 5. Inizializza i vettori di stringhe figura e segno
25 creaMazzo( mazzo, figura, segno ); 26 mischiaMazzo( mazzo ); 27 daiCarte( mazzo ); 28 return 0; 29 } 30 31 void creaMazzo( Carta * const wMazzo, constchar * wFigura[], Mette tutte le 52 carte nel mazzo in modo ordinato. Le 12 figure e i 4 segni vengono determinati da divisione/resto progressivi per 13. Si va dell’asso fino al re per ogni segno, finite le carte di un segno si ripete per gli altri segni rimasti. 32 constchar * wSegno[] ){ 33 int i; 34 35 for ( i = 0; i <= 51; i++ ) { 36 wMazzo[ i ].figura = wFigura[ i % 13 ]; 37 wMazzo[ i ].segno = wSegno[ i / 13 ]; 38 } 39 } 23 40 24 srand( time( NULL ) ); 10.7 - Caso di studio: un efficiente mescolatore/distributore di carte (2/4) 6. Chiama in sequenza le funzioni per creare e mischiare il mazzo e per distriuire le carte: mazzo, figura e segno sono vettori e passano quindi per riferimento 7. Inizializza il vettore mazzo assegnando figura/segno ai membri di ogni suo elemento 8. I parametri formali di mazzo, figura e segno sono vettori e possono quindi esser dichiarati come vettori o come puntatori al tipo di dato dei vettori stessi
52 53 voiddaiCarte( constCarta * const wMazzo ){ Seleziona un numero casuale tra 0 e 51. Scambia l’elemento i con quell’elemento. 54 int i; 55 56 for ( i = 0; i <= 51; i++ ) 57 printf( "%5s of %-8s%c", wMazzo[ i ].figura, Fa il ciclo utilizzando gli array e visualizza i 2 mazzi distribuiti 58 wMazzo[ i ].segno, ( i + 1 ) % 2 ? '\t' : '\n' ); 59 } 41 voidmischiaMazzo( Carta * const wMazzo ){ 42 int i, j; 43 Carta temp; 44 45 for ( i = 0; i <= 51; i++ ){ 46 j = rand() % 52; 47 temp = wMazzo[ i ]; 48 wMazzo[ i ] = wMazzo[ j ]; 49 wMazzo[ j ] = temp; 50 } 51 } 10.7 - Caso di studio: un efficiente mescolatore/distributore di carte (3/4) 9. Mischia il mazzo che inizialmente era stato creato ordinato 10. Per ogni posizione all’interno del mazzo genera un numero casuale tra 0 e 51 che indica un’altra posizione nel mazzo e scambia la carta in posizione corrente con quella nell’altra posizione casuale 11. Distribuisce le carte del mazzo in modo analogo all’esempio già visto, ma figura e segno sono presi dai membri degli elementi del vettore mazzo
10.7 - Caso di studio: un efficiente mescolatore/distributore di carte (4/4) Visualizzazione del Programma Otto di Quadri Asso di Cuori Otto di Fiori Cinque di Picche Sette di Cuori Due di Quadri Asso di Fiori Dieci di Quadri Due di Picche Sei di Quadri Sette di Picche Due di Fiori Fante di Fiori Dieci di Picche Re di Cuori Fante di Quadri Tre di Cuori Tre di Quadri Tre di Fiori Nove di Fiori Dieci di Cuori Due di Cuori Dieci di Fiori Sette di Quadri Sei di Fiori Regina di Picche Sei di Cuori Tre di Picche Nove di Quadri Asso di Quadri Fante di Picche Cinque di Fiori Re di Quadri Sette di Fiori Nove di Picche Quattro di Cuori Sei di Picche Otto di Picche Regina di Quadri Cinque di Quadri Asso di Picche Nove di Cuori Re di Fiori Cinque di Cuori Re di Picche Quattro di Quadri Regina di Cuori Otto di Cuori Quattro di Picche Fante di Cuori Quattro di Fiori Regina di Fiori
10.8 - Le Unioni (1/2) • union • Sono locazioni di memoria che contengono diversi tipi di dato nel tempo • Tutti i membri di una union condividono lo stesso spazio di memoria • Quando viene dichiarata una union, di fatto viene allocato uno spazio di memoria pari all'elemento più grande della union stessa • A run-time, la memoria contiene il valore di un solo membro alla volta • Si può accedere/usare solo l’ultimo membro assegnato (a cui si è dato un valore), tutti quelli precedenti si perdono perché lo spazio è condiviso • Le operazioni sulle union sono simili a quelle delle struct • Assegnamenti tra union di pari tipo di struttura, recupero dei loro indirizzi (&), accesso ai membri direttamente con (..) o tramite puntatore (->) • Dichiarare una union • Si dichiara nello stesso modo delle struct union Numero{ int x; float y; }; union Numero valore;
1 /* Fig. 10.5: fig10_05.c - Un esempio di unione */ 3 #include <stdio.h> 4 5 unionnumero{ int x; double y; }; 6 7 int main(){ 8 unionnumero valore; 9 10 valore.x = 100; 11 printf( "%s\n%s%d\n%s%f\n\n", “Inserisci un valore nel membro” 12 “intero e visualizza entrambi i membri.”, 13 "int: ", valore.x, "double: ", valore.y ); 14 valore.y = 100.0; 15 printf( "%s\n%s%d\n%s%f\n", “Inserisci un valore nel membro” 16 “decimale e visualizza entrambi i membri.", 17 "int: ", valore.x, "double: ", valore.y ); 18 return 0; 19 } 10.8 - Le Unioni (2/2) 1. Definizione della union 2. Assegnamento del primo membro della union (un intero) 3. Visualizzazione del membro intero 4. Assegnamento del secondo membro (un double) che di fatto sostiuisce il primo Visualizzazione del Programma Inserisci un valore nel membro intero e visualizza entrambi i membri. int: 100 double: -92559592117433136000000000000000000000000000000000000000000000.00000 Inserisci un valore nel membro decimale e visualizza entrambi i membri. int: 0 double: 100.000000
10.9 - Le costanti di Enumerazione (1/2) • Enumerazione (enum) • E’ un tipo di dato che può assumere solo valori all’interno di un insieme di costanti intere rappresentate da identificatori • Sono come delle costanti simboliche i cui valori vengono impostati automaticamente • I loro valori partono per default da 0 e sono incrementati di 1 • I loro valori possono anche essere assegnati esplicitamente con l’operatore = • E’ necessario usare nomi di costanti unici, mai ripeterli nell’enumerazione • Sintassi (esempio): enum Mesi { GEN = 1, FEB, MAR, APR, MAG, GIU, LUG, AGO, SET, OTT, NOV, DIC} mese; • Crea un nuovo tipo enum Mesi dove gli identificatori sono impostati a interi da 1 a 12, dove l’enumerazione inizia da 1 e incrementa di 1 ogni volta • Dichiara una variabile “mese” di tipo enum, che potrà valere GEN, FEB, … • Il vantaggio è dato dall’uso del nome di costante al posto del valore che intero che essa rappresenta (frequente l’uso nei cicli) • Alle variabili di tipo enum si possono assegnare solamente valori pari agli identificatori dell’enumerazione (mai i rispettivi interi corrispondenti) • Quando invece vengono lette, viene restituito il rispettivo valore intero
1 /* Fig. 10.18: fig10_18.c - Usare il tipo enumerazione */ 2 #include <stdio.h> 3 4 enum Mesi{ GEN = 1,FEB,MAR,APR,MAG,GIU,LUG,AGO,SET,OTT,NOV,DIC }; 5 6 int main(){ 7 enum Mesi mese; 8 constchar *nomeMese[] = { "", “Gennaio", “Febbraio", "Marzo", 9 "Aprile", "Maggio", “Giugno","Agosto", "Settembre", 10 "Ottobre", “Luglio", "Novembre", "Dicembre" }; 11 12 for (mese=GEN; mese<=DIC; mese++) 13 printf( "%2d%11s\n", mese, nomeMese[mese] ); 14 return 0; 15 } 10.9 - Le costanti di Enumerazione (2/2) 1. Definizione della enumerazione 2. Inizializzazione delle variabili 3. Ciclo di stampa della enumerazione Visualizzazione del programma 1 Gennaio 2 Febbraio 3 Marzo 4 Aprile 5 Maggio 6 Giugno 7 Luglio 8 Agosto 9 Settembre 10 Ottobre 11 Novembre 12 Dicembre