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
 2000 Prentice Hall, Inc. All rights reserved.
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)
 2000 Prentice Hall, Inc. All rights reserved.
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)
 2000 Prentice Hall, Inc. All rights reserved.
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)
 2000 Prentice Hall, Inc. All rights reserved.
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”;
 2000 Prentice Hall, Inc. All rights reserved.
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
struct carta *miaCartaPtr = &miaCarta;
printf( "%s", miaCartaPtr->segno );
– I due tipi di accesso sono equivalenti
• Ovvero miaCartaPtr->segno è alias di (*miaCartaPtr).segno
 2000 Prentice Hall, Inc. All rights reserved.
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
 2000 Prentice Hall, Inc. All rights reserved.
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
 2000 Prentice Hall, Inc. All rights reserved.
10.7 - Caso di studio: un efficiente
mescolatore/distributore di carte (1/4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Fig. 10.3: fig10_03.c
Il programma per mescolare e distribuire carte usando strutture */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct carta{
const char *figura;
const char *segno;
};
typedef struct carta Carta;
void creaMazzo( Carta * const, const char *[], const char *[] );
void mischiaMazzo( Carta * const );
void daiCarte( const Carta * const );
int main(){
Carta mazzo[ 52 ];
const char *figura[] = { "Asso", "Due", "Tre", “Quattro",
“Cinque", "Sei", "Sette", “Otto", "Nove",
“Dieci", "Jack", “Regina", “Re"};
const char *segno[] = { “Cuori", “Quadri", “Fiori", “Picche"};
 2000 Prentice Hall, Inc. All rights reserved.
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
10.7 - Caso di studio: un efficiente
mescolatore/distributore di carte (2/4)
23
6. Chiama in sequenza
24
srand( time( NULL ) );
le funzioni per creare
25
creaMazzo( mazzo, figura, segno );
e mischiare il mazzo e
26
mischiaMazzo( mazzo );
per distriuire le carte:
27
daiCarte( mazzo );
mazzo, figura e segno
28
return 0;
sono vettori e passano
29 }
quindi per riferimento
30
31 void creaMazzo( Carta * const wMazzo, const char * wFigura[],
7. Inizializza il vettore
32
const char * wSegno[] ){
Mette tutte le 52 carte nel mazzo
mazzo assegnando
33
int i;
in modo ordinato. Le 12 figurefigura/segno
ei
ai membri
34
di ogni suo elemento
4 segni vengono determinati da
35
for ( i = 0; i <= 51; i++ ) {
36
wMazzo[ i ].figura = wFigura[ i % 13 ];divisione/resto progressivi per 13.
8. I parametri formali di
37
wMazzo[ i ].segno = wSegno[ i / 13 ]; Si va dell’asso fino al re per ogni
mazzo, figura e segno
segno, finite le carte di un segno
38
}
sono vettori e possono
39 }
si ripete per gli altri segni rimasti.
quindi esser dichiarati
40
come vettori o come
puntatori al tipo di
dato dei vettori stessi
 2000 Prentice Hall, Inc. All rights reserved.
10.7 - Caso di studio: un efficiente
mescolatore/distributore di carte (3/4)
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
void mischiaMazzo( Carta * const wMazzo ){
int i, j;
Carta temp;
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
Seleziona un numero casuale tra 0 e 51.
che indica un’altra
Scambia l’elemento i con quell’elemento. posizione nel mazzo
e scambia la carta in
posizione corrente
}
con quella nell’altra
Fa
il
ciclo
utilizzando
gli
array
e
posizione
casuale
void daiCarte( const Carta * const wMazzo ){
visualizza i 2 mazzi distribuiti
int i;
11. Distribuisce le carte
del mazzo in modo
for ( i = 0; i <= 51; i++ )
analogo all’esempio
printf( "%5s of %-8s%c", wMazzo[ i ].figura,
già visto, ma figura e
wMazzo[ i ].segno, ( i + 1 ) % 2 ? '\t' : '\n' );
segno sono presi dai
}
membri degli elementi
del vettore mazzo
for ( i = 0; i <= 51; i++ ){
j = rand() % 52;
temp = wMazzo[ i ];
wMazzo[ i ] = wMazzo[ j ];
wMazzo[ j ] = temp;
}
 2000 Prentice Hall, Inc. All rights reserved.
10.7 - Caso di studio: un efficiente
mescolatore/distributore di carte (4/4)
Otto
Otto
Sette
Asso
Due
Sette
Fante
Re
Tre
Tre
Dieci
Dieci
Sei
Sei
Nove
Fante
Re
Nove
Sei
Regina
Asso
Re
Re
Regina
Quattro
Quattro
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
Quadri
Fiori
Cuori
Fiori
Picche
Picche
Fiori
Cuori
Cuori
Fiori
Cuori
Fiori
Fiori
Cuori
Quadri
Picche
Quadri
Picche
Picche
Quadri
Picche
Fiori
Picche
Cuori
Picche
Fiori
Asso
Cinque
Due
Dieci
Sei
Due
Dieci
Fante
Tre
Nove
Due
Sette
Regina
Tre
Asso
Cinque
Sette
Quattro
Otto
Cinque
Nove
Cinque
Quattro
Otto
Fante
Regina
 2000 Prentice Hall, Inc. All rights reserved.
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
di
Cuori
Picche
Quadri
Quadri
Quadri
Fiori
Picche
Quadri
Quadri
Fiori
Cuori
Quadri
Picche
Picche
Quadri
Fiori
Fiori
Cuori
Picche
Quadri
Cuori
Cuori
Quadri
Cuori
Cuori
Fiori
Visualizzazione
del Programma
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;
 2000 Prentice Hall, Inc. All rights reserved.
10.8 - Le Unioni (2/2)
1 /* Fig. 10.5: fig10_05.c - Un esempio di unione */
3 #include <stdio.h>
4
5 union numero{ int x; double y; };
6
7 int main(){
8
union numero valore;
9
10
11
12
13
14
15
16
17
18
19 }
valore.x = 100;
printf( "%s\n%s%d\n%s%f\n\n", “Inserisci un valore nel membro”
“intero e visualizza entrambi i membri.”,
"int: ", valore.x, "double: ", valore.y );
valore.y = 100.0;
printf( "%s\n%s%d\n%s%f\n", “Inserisci un valore nel membro”
“decimale e visualizza entrambi i membri.",
"int: ", valore.x, "double: ", valore.y );
return 0;
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
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
 2000 Prentice Hall, Inc. All rights reserved.
Visualizzazione
del Programma
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
 2000 Prentice Hall, Inc. All rights reserved.
10.9 - Le costanti di Enumerazione (2/2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
/* Fig. 10.18: fig10_18.c - Usare il tipo enumerazione */
#include <stdio.h>
enum Mesi{ GEN = 1,FEB,MAR,APR,MAG,GIU,LUG,AGO,SET,OTT,NOV,DIC };
int main(){
enum Mesi mese;
const char *nomeMese[] = { "", “Gennaio", “Febbraio", "Marzo",
"Aprile", "Maggio", “Giugno", "Agosto", "Settembre",
"Ottobre", “Luglio", "Novembre", "Dicembre" };
for (mese=GEN; mese<=DIC; mese++)
printf( "%2d%11s\n", mese, nomeMese[mese] );
return 0;
1. Definizione della
enumerazione
2. Inizializzazione delle
variabili
3. Ciclo di stampa della
enumerazione
}
Gennaio
Febbraio
Marzo
Aprile
Maggio
Giugno
Luglio
Agosto
Settembre
Ottobre
Novembre
Dicembre
 2000 Prentice Hall, Inc. All rights reserved.
Visualizzazione
del programma
Scarica

Cap. 10 Deitel

ppt

ppt