Title: Nessun titolo diapositiva
1Le strutture e le unioni in C
2Sommario
- Le strutture e le unioni
- Le liste concatenate
- Allocazione dinamica
- Creazione ed aggiunta di un nuovo elemento alla
lista - Inserimento e cancellazione di elementi
- La ricerca di un elemento
2
3Le strutture e le unioni
3
4Introduzione
- Gli array sono uno strumento adeguato per il
trattamento di insiemi di variabili di uno stesso
tipo - Quando i tipi di dati, che devono essere
logicamente aggregati, sono disomogenei, è
necessario usare il tipo struttura (o record) - Lulteriore tipo composto unione consente di
interpretare il contenuto delle stesse locazioni
di memoria con modalità diverse (è la
realizzazione del concetto di record a lunghezza
variabile)
4
5Le strutture ? 1
- Esempio Supponiamo di voler memorizzare,
relativamente a ciascun abitante di un dato
comune, nome e cognome, data di nascita e codice
fiscale - Nome e cognome costituiscono un array di
caratteri - La data è composta da tre numeri interi, che
descrivono giorno, mese ed anno - Il codice fiscale è un array di 16 caratteri (15
per il codice ed uno per il carattere nullo di
terminazione) - Le informazioni non possono essere collezionate
in un unico array di caratteri, perché sono
disomogenee
5
6Le strutture ? 2
- Possibile soluzione memorizzare le informazioni
in variabili distinte - Unorganizzazione più naturale consiste nella
definizione di una variabile che contenga tutti i
dati relativi ad una persona utilizzando un tipo
struttura - Una struttura è simile ad un array, ma è
costituita da campi (piuttosto che da elementi)
che sono identificati da nomi (piuttosto che da
indici) e possono contenere informazione di tipo
diverso
6
7Le strutture ? 3
Esempio
- Vi sono due dichiarazioni la prima contiene lo
schema della struttura vitalstat, la seconda
dichiara una variabile vs di tipo struct
vitalstat - Nota includere un prefisso nei nomi dei campi,
per distinguerli da campi di strutture diverse
struct vitalstat char vs_name20,vs_fiscode
16 short vs_day,vs_month,vs_year struct
vitalstat vs
- Lallocazione della struttura vs sulla macchina
di riferimento presuppone che i campi siano
memorizzati consecutivamente, nellordine in cui
sono dichiarati, ma non necessariamente in
maniera contigua
vs_name
vs_fiscode
vs_day
vs_month
vs_year
7
8Le strutture ? 4
- Il nome vitalstat è unetichetta (o tag) e struct
vitalstat rappresenta un nuovo tipo di dati
definito dallutente, per il quale non viene
riservata memoria - Letichetta può essere utilizzata ogni volta che
è necessario creare ulteriori variabili che
contengono gli stessi campi - La sintassi della dichiarazione di una struttura
può assumere diverse forme - Dichiarazione delletichetta e uso delletichetta
(insieme alla parola chiave struct) per definire
le variabili - Uso di typedef per definire un particolare tipo
struttura
struct vitalstat vsa1000, pvs pvs ? vsa10
8
9Le strutture ? 5
- Il tipo VITALSTAT rappresenta lintera struttura,
compresa la parola chiave struct
- Il nome di tipo è scritto maiuscolo, per
distinguerlo dai nomi di etichette e variabili - Unetichetta o un typedef consentono di definire
una sola volta lo schema di una struttura, per
dichiarare variabili del tipo struttura quando
necessario
typedef struct char vs_name20,vs_fiscode1
6 short vs_day, vs_month, vs_year
VITALSTAT
- Le dichiarazioni di struttura sono normalmente
raggruppate in file header, così da poter essere
accedute da più file sorgente
9
10Linizializzazione di strutture ? 1
- Una struttura può essere inizializzata facendo
seguire al nome della variabile di tipo struttura
il simbolo di uguale e la lista dei valori
iniziali racchiusi tra parentesi graffe - Ogni valore iniziale deve essere dello stesso
tipo del corrispondente campo della struttura
VITALSTAT vs ? Marco Rossi,
MRCRSS91C23D612K, 23, 3, 1991
10
11Linizializzazione di strutture ? 2
- Un valore iniziale non può essere incluso in una
dichiarazione che contiene solo unetichetta o un
typedef, poiché tali dichiarazioni definiscono lo
schema della struttura, ma non riservano memoria
typedef struct int a float b s ?
1, 1.0 / non ammesso /
11
12Laccesso ai campi della struttura
- Esistono due modalità diverse di accesso ai campi
di una struttura, dipendenti dalla modalità di
accesso alla struttura, diretta o mediante
puntatore - Nel caso di accesso diretto alla struttura, si
usano il nome della struttura ed il nome del
campo, separati dalloperatore punto . - Nel caso di accesso tramite puntatore alla
struttura, si usa loperatore freccia destra
?gt, formato dalla concatenazione del segno meno
e del simbolo maggiore - Loperatore ?gt è una forma abbreviata per
accedere allindirizzo contenuto nel puntatore e
quindi applicare loperatore punto
VITALSTAT pvs pvs?gtvs_day ? 23
pvs?gtvs_month ? 3 pvs?gtvs_year ? 1991
vs.vs_day ? 23 vs.vs_month ? 3 vs.vs_year ?
1991
pvs?gtvs_day è equivalente a (pvs).vs_day
12
13Gli array di strutture
- Gli array di strutture vengono dichiarati facendo
precedere al nome dellarray il nome typedef
della struttura
- Esempio Funzione che calcola il numero di
persone con età compresa in un intervallo
specificato - Le variabili locali p e p_last sono state
introdot-te per mantenere invariato il valore del
parametro formale vsa (che altrimenti potrebbe
essere usato direttamente nel ciclo)
?include v_stat.h / file header che
contiene la dichiarazione typedef VITALSTAT
/ int agecount(vsa,size,low_age,high_age,current
_year) VITALSTAT vsa int size, low_age,
high_age, current_year int age, count?0
VITALSTAT p?vsa, p_last?vsasize
for ( pltp_last p??)
age?current_year ? p?gtvs_year if
((age gt? low_age) (age lt? high_age))
count?? return count
13
14Le strutture innestate ? 1
- Una struttura innestata è una struttura in cui
almeno uno dei campi è, a sua volta, una
struttura - Le strutture innestate sono comuni in C perché
consentono di creare gerarchie di dati
typedef struct char day char
month short year DATE typedef
struct char vs_name20, vs_fiscode16
DATE vs_birth_date VITALSTAT
typedef struct char vs_name20,
vs_fiscode16 struct vs_birth_date
short vs_day short vs_month
short vs_year VITALSTAT
Per identificare lanno di nascita in una
struttura vs dichiarata VITALSTAT, si deve
scrivere vs.vs_birth_date.vs_year
14
15Le strutture innestate ? 2
- Una struttura innestata viene inizializzata
racchiudendo i valori iniziali fra parentesi
graffe - Non esistono limiti al numero di livelli di
nesting delle strutture, anche se i riferimenti
agli elementi diventano sempre più difficili da
leggere perché contengono i nomi di tutte le
strutture intermedie
typedef struct DATE d char
event20 CALENDAR CALENDAR holiday ? 25,
12, 2010, Natale
15
16Le strutture contenenti puntatori a sé stesse ? 1
- Le strutture e le unioni non possono contenere
istanze di sé stesse, ma possono contenere
puntatori a sé stesse - Il compilatore consente di dichiarare un
puntatore ad una struttura prima che sia stata
dichiarata la struttura
struct s int a, b float c
struct s pointer_to_s
16
17Le strutture contenenti puntatori a sé stesse ? 2
- Il riferimento in avanti a strutture (e unioni) è
uno dei pochi casi nel linguaggio C in cui un
identificatore può essere utilizzato prima di
essere dichiarato - Il riferimento in avanti non è ammesso con la
definizione di tipo (typedef)
typedef struct neg int a FOO p /
errato perché FOO non è ancora
stato dichiarato / FOO
17
18Lallineamento dei campi delle strutture ? 1
- Alcune architetture (come i processori Motorola)
richiedono che ogni oggetto di dimensioni
superiori ad un char venga memorizzato in
locazioni di memoria con indirizzo pari - I problemi di allineamento non sono normalmente
visibili al programmatore, ma possono creare
spazi vuoti allinterno delle strutture
struct align_examp char mem1 short
mem2 char mem3 s1
1005
18
19 Lallineamento dei campi delle strutture ? 2
- Gli spazi vuoti vengono eliminati da una
redistribuzione delle dichiarazioni degli
elementi - Poiché le strutture possono essere memorizzate in
modi diversi sui diversi calcolatori, è
necessario accedervi con modalità portabili - Lallineamento naturale (tutti gli oggetti di 2
byte iniziano da indirizzi pari, gli oggetti di 4
byte a indirizzi divisibili per quattro, etc.) è
il requisito più stringente imposto dai
calcolatori - Se la struttura è allineata in modo naturale è
portabile
struct align_examp char mem1, mem3
short mem2 s1
19
20Il passaggio di strutture come parametri di
funzione ? 1
- Esistono due modalità per passare strutture come
argomenti di funzione - Passaggio della struttura vera e propria, per
valore - Passaggio di un puntatore alla struttura, per
indirizzo - Il passaggio per indirizzo è più efficiente
perché solo il puntatore viene copiato nellarea
di memoria degli argomenti, mentre il passaggio
per valore richiede la copia dellintera
struttura
VITALSTAT vs func1(vs) / Passaggio
per valore / func2(vs) / Passaggio
per indirizzo /
20
21Il passaggio di strutture come parametri di
funzione ? 2
- Le strutture dovrebbero essere passate per valore
solo se - la struttura è molto piccola (di dimensione
paragonabile al puntatore) - è necessario garantire che la funzione chiamata
non ne alteri il contenuto originale - In dipendenza della modalità di passaggio
prescelta, occorre dichiarare il parametro
allinterno della funzione come struttura o come
puntatore a struttura
void func1(vs) VITALSTAT vs / largomento è
una struttura /
void func2(pvs) VITALSTAT pvs / largomento è
un puntatore a struttura /
21
22Il passaggio di strutture come parametri di
funzione ? 3
- La scelta della modalità di passaggio dei
parametri struttura definisce gli operatori da
impiegare allinterno della funzione chiamata - il punto se la struttura è passata per valore
- la freccia destra se è passata per indirizzo
22
23Il passaggio di strutture ed array come parametri
di funzione ? 1
- Le modalità per il passaggio di strutture e di
array come parametri di funzione non sono
omogenee - Per passare un array, si specifica il solo nome
dellarray senza indici il compilatore
interpreta il nome come un puntatore al primo
elemento dellarray, che viene passato per
indirizzo - Non è possibile passare un array per valore, se
non includendolo in una struttura passata per
valore - Per le strutture, il nome viene interpretato come
lintera struttura - Applicando la stessa sintassi si ottiene una
semantica diversa
int ar100 struct tag st funcv(ar) /
viene passato un puntatore al primo elemento di
ar / funcs(st) / viene passata
lintera struttura /
23
24Il passaggio di strutture ed array come parametri
di funzione ? 2
- La stessa inconsistenza si riproduce anche
allinterno delle funzioni - Le due versioni basate su array sono equivalenti
- Le due versioni basate su strutture non lo sono
void funcv(ar) int ar / ar viene
convertito in puntatore ad intero / void
funcv(ar) int ar / ar è un puntatore ad
intero /
void funcs(st) struct tag st / st è una
struttura completa / void funcs1(st) struct
tag st / st è un puntatore a struttura /
24
25Strutture restituite da funzioni ? 1
- Le funzioni possono restituire strutture o
puntatori a strutture - La dichiarazione del tipo di dato restituito da
una funzione deve essere concorde con il valore
effettivamente restituito - Generalmente, è preferibile restituire puntatori
a strutture per motivi di efficienza - Se si restituisce un puntatore a struttura, la
struttura deve avere durata fissa, perché
altrimenti non sarebbe più accessibile al termine
della funzione - La restituzione di strutture è particolarmente
utile quando si voglia comunicare allesterno più
di un valore
25
26Strutture restituite da funzioni ? 2
?include ltstdio.hgt ?include ltmath.hgt ?define
TOO_LARGE 100 / dipendente dalla macchina
/ ?define NULL (void ) 0 typedef struct
double sine, cosine, tangent TRIG TRIG
get_trigvals(radian_val) double radian_val
static TRIG result / se radian_val è
troppo grande, i valori sin, cos, tan non sono
significativi / if (radian_val gt
TOO_LARGE) printf(Valore
di ingresso troppo grande) return
NULL result.sine ?
sin(radian_val) result.cosine ?
cos(radian_val) result.tangent ?
tan(radian_val) return (result)
26
27Lassegnamento di strutture
- Lo standard ANSI consente di assegnare una
struttura ad una variabile struttura dello stesso
tipo
struct int a float b s1, s2,
sf(), ps s1 ? s2 s2 ? sf() ps ? s1 s2
? ps
27
28Le unioni ? 1
- Le unioni consentono agli elementi di sovrapporsi
luno sullaltro per condividere la stessa area
di memoria - Esempio
- Il compilatore riserva sempre una quantità di
memoria sufficiente allelemento più grande e
tutti gli elementi iniziano allo stesso indirizzo
typedef union struct char c1, c2
s long j float x U
28
29Esempio sulle unioni ? 1
- I dati viaggiano sulla linea un byte alla volta
le unioni consentono di raggruppare i byte in
modo tale che i dati possano essere ricostruiti
nella loro forma originale - Sia get_byte() una funzione che restituisce un
singolo byte prelevato da un dispositivo di
comunicazione - Un valore double a otto byte può essere estratto
dal dispositivo mediante otto chiamate successive
alla funzione get_byte()
29
30Esempio sulle unioni ? 2
- I caratteri ricevuti vengono memo-rizzati in
elementi successivi di c il valore del numero
double si ottiene accedendo al campo val
dellunione - Laccesso ai campi di ununione non ha impatto
alcuno sui bit contenuti in memoria (non si
effettuano conversioni di tipo) il compilatore
interpreta in modo diverso gli stessi bit
union doub char c8 double
val double get_double() extern char
get_byte() int j union doub d for
(j?0 jlt8 j??) d.cj ? get_byte()
return d.val
30
31Le liste concatenate
31
32Allocazione dinamica ? 1
- Finora, per gestire insiemi di dati, sono stati
utilizzati array (di strutture) - Tale approccio è valido se si conosce a priori il
numero di elementi che si devono gestire - Viceversa, lutilizzo di array può essere
estremamente oneroso, perché rende necessaria
lallocazione di una quantità di memoria
sufficiente a memorizzare il caso peggiore - Parte della memoria può andare sprecata (perché
inutilizzabile per scopi diversi) - Non è possibile accedere ad una quantità di
memoria maggiore di quella allocata inizialmente
32
33Allocazione dinamica ? 2
- La soluzione consiste nellallocare memoria in
modo dinamico, con malloc() o calloc() ? che,
tuttavia, non garantiscono la contiguità fisica
per elementi logicamente contigui ? e
nellutilizzare liste concatenate - Il modo più comune per garantire la contiguità
logica di strutture allocate dinamicamente è
infatti luso delle liste semplicemente/doppiament
e concatenate
33
34Le liste concatenate ? 1
- In una lista concatenata, ogni struttura contiene
un elemento aggiuntivo (un puntatore) che punta
alla struttura successiva - Esistono liste semplici e liste doppiamente
concatenate, in cui ogni struttura possiede due
puntatori, rispettivamente allelemento
precedente ed al successivo
typedef struct vitalstat char vs_name20,
vs_fiscode16 unsigned int vs_day,
vs_month, vs_year
struct vitalstat next VITALSTAT
34
35Le liste concatenate ? 2
- Le operazioni che si effettuano sulle liste sono
- Creazione di un elemento
- Aggiunta di un elemento alla fine della lista
- Inserimento di un elemento in una lista
- Cancellazione di un elemento da una lista
- Ricerca di un elemento in una lista
- Tutte le operazioni, tranne la ricerca, sono
indipen-denti dal contenuto informativo delle
strutture (dai campi dati) e possono essere
formulate in modo generale
35
36La creazione di un elemento ? 1
- Per creare un elemento di una lista, è
sufficiente riservare memoria per la struttura e
restituire un puntatore allarea allocata
?include v_stat.h ?include ltstdlib.hgt ELEMENT
create_list_element() ELEMENT p
p ? (ELEMENT )malloc(sizeof(ELEMENT))
if (p ?? NULL)
printf(create_list_element malloc non
riuscita.\n) exit(1) p ?gt
next ? NULL return p
Alloca la memoria ad un elemento della lista, ma
non lo collega alla lista stessa
36
37La creazione di un elemento ? 2
- Note
- Il nome ELEMENT rende la funzione indipendente
dal tipo di dati effettivamente gestiti - Per utilizzare la funzione create_list_element()
in relazione alla struttura vitalstat, è
necessario includere nel file v_stat.h (che
contiene la definizione del tipo struttura) le
ulteriori dichiarazioni - ELEMENT diviene un sinonimo di struct vitalstat
- Per dichiarare il puntatore occorre usare
unetichetta invece di un typedef è la sola
modalità con cui una struttura può fare
riferimento a sé stessa, dato che il nome di
typedef non è definito fino al termine della
dichiarazione
?define NULL (void ) 0 typedef struct vitalstat
ELEMENT
37
38Laggiunta di un nuovo elemento ? 1
?include v_stat.h static ELEMENT head void
add_element(e) ELEMENT e ELEMENT p /
se il primo elemento non esiste, viene creato
/ if (head ?? NULL) head ? e
return / altrimenti, trova lultimo
elemento / for (p?head p?gtnext !? NULL
p?p?gtnext) / istruzione vuota /
p ?gt next ? e
- La variabile head è un puntatore allinizio della
lista ed è dichiarata con ambito di visibilità a
li-vello di file, per renderla disponibile a più
funzioni - Nel ciclo for, la scan-sione della lista è basata
sul controllo del valore di p-gtnext se è
diver-so da NULL, esiste un elemento successivo,
al-trimenti è stata rag-giunta la fine della
lista
38
39Laggiunta di un nuovo elemento ? 2
- Lassegnamento
- p?gtnext ? e
- aggiunge una nuova struttura alla fine della
lista - Largomento e della funzione è un puntatore alla
struttura che è stata allocata dalla funzione
chiamante - Esempio Creare una lista concatenata di dieci
strutture vitalstat
?include v_stat.h static ELEMENT head
main() for (j?0 jlt10 j??)
add_element(create_list_element())
39
40Linserimento di un elemento
- Per inserire un elemento in una lista
concatenata, deve essere specificata la posizione
in cui deve avvenire linserimento
?include v_stat.h void insert_after(p,q) /
inserimento di p dopo q / ELEMENT p, q /
controllo di validità degli argomenti /
if((p??NULL)) (q ?? NULL) (p ?? q)
(q?gtnext ?? p))
printf(insert_after argomenti errati.\n)
return p ?gt next ? q ?gt
next q ?gt next ? p
40
41La cancellazione di un elemento ? 1
- Per la cancellazione di un elemento da una lista
concatenata, occorre individuare lelemento che
precede quello da eliminare per riallacciare i
puntatori della lista dopo leliminazione - Inoltre, è necessario utilizzare la funzione
free() per rilasciare la memoria dellelemento
eliminato
41
42La cancellazione di un elemento ? 2
?include v_stat.h static ELEMENT head void
delete_element(goner) ELEMENT goner
ELEMENT p if (goner ?? head) head
? goner?gtnext else for
(p?head(p!?NULL) (p?gtnext!?goner)p?p?gtnext)
/ istruzione vuota /
if (p ?? NULL)
printf(delete_element elemento non contenuto
nella lista.\n) return
p?gtnext ? p?gtnext?gtnext
free(goner)
Loperatore freccia destra ha associatività
sinistra e pertanto lespressione p?gtnext?gtnext vi
ene valutata come (p?gtnext)?gtnext
42
43La ricerca di un elemento ? 1
- La funzione di ricerca di un elemento della lista
viene fatta in base al contenuto di uno o più
campi della struttura - Esempio Funzione che ricerca, fra strutture di
tipo vitalstat, un elemento per cui il valore del
campo name coincida con largomento della funzione
43
44La ricerca di un elemento ? 2
?include v_stat.h ?include ltstring.hgt static
ELEMENT head ELEMENT find(aname) char
aname ELEMENT p for (p
? head p !? NULL p ? p?gtnext) if
(strcmp(p?gtvs_name, aname)??0)
return p return NULL
44
45Costruzione di una lista ordinata ? 1
/ Letta in input una sequenza di numeri
interi la memorizza in una lista in ordine
crescente. / include ltstdlib.hgt include
ltstdio.hgt / struttura per la
rappresentazione dei nodi della lista /
struct nodo int info struct nodo
next
/ Funzione principale. / main(void)
struct nodo primo primo?leggi_lista_ordin
ata() stampa_lista(primo) exit(0)
45
46Costruzione di una lista ordinata ? 2
/ Legge una sequenza di numeri interi e li
memorizza in una lista ordinata (in ordine
crescente). Restituisce il puntatore al primo
elemento della lista. / struct nodo
leggi_lista_ordinata(void) struct nodo
p, p1, primo, prec int i, n, num
primo NULL printf(Inserire numero di
elementi ) scanf(d, n) for (i?0
iltn i??) scanf(d, num)
p ? malloc(sizeof(struct nodo))
p-gtinfo ? num p1 ? primo prec
? NULL
while((p1!?NULL)(p1-gtinfoltp-gti
nfo)) prec ? p1 p1 ?
p1-gtnext if (prec !? NULL)
prec-gtnext ? p else primo ?
p p-gtnext ? p1 return(primo)
46
47Costruzione di una lista ordinata ? 3
/ Stampa la lista, a partire dallelemento
puntato dal puntatore p, ricevuto come
parametro. / void stampa_lista(p) struct
nodo p while (p !? NULL)
printf(d ---gt , p-gtinfo) p ?
p-gtnext printf(NULL\n) return
47