Sei sulla pagina 1di 10

2

Tipi di dati aggregati


 Il termine tipo aggregato si riferisce ai
vettori e ai tipi struct:
Strutture  un vettore è un raggruppamento di variabili dello
stesso tipo
 una struct è un raggruppamento di variabili
anche di tipo diverso
Ver. 2.4

© 2010 - Claudio Fornaro - Corso di programmazione in C

3 4

Tipo struct Tipo struct


 Insieme di più variabili denominate membri  Esempio
in genere di tipo diverso identificate da un (supponendo short su 16 bit e long su 32
nome comune (tag) in una macchina con allineamento a 32 bit):
 In memoria i membri sono allocati short inutilizzato long
contiguamente e nello stesso ordine di
dichiarazione 32 bit 32 bit
 Tra un membro e il successivo possono  Non c’è mai spazio di allineamento prima del
esserci (dipende dal tipo di microprocessore) primo membro, quindi l’indirizzo di una
spazi intermedi di allineamento della memoria variabile struct coincide con quello al suo
non indirizzabili (quindi inutilizzabili) e dal primo membro
contenuto indefinito
5 6

Dichiarazione di struct Dichiarazione di struct


struct nome_tag  Lo scope del nome dei membri è confinato alla
{ sola struttura dove sono dichiarati: le variabili
tipo1 nome_membro1; del programma e i membri di altre strutture
tipo2 nome_membro2;
... possono avere gli stessi nomi di un membro
};  Esempio
 La dichiarazione non riserva memoria, ma crea struct punto x y
un nuovo tipo di dato {
int x;
 Dichiarazioni di struct con contenuto identico int y;
ma diverso tag sono considerate di tipo diverso };
 Una dichiarazione di struct senza tag
dove:
(anonima ) è sempre considerata avente tipo  punto è il tag
diverso da quello di ogni altra struct (con tag
 x e y sono i due membri, scalari di tipo int
o anonima), anche se ha gli stessi membri

7 8

Definizione di variabili struct Definizione di variabili struct


 Riserva memoria  La definizione può essere separata dalla
 Ha la consueta forma: dichiarazione del tipo (tag non omesso)
tipo var1, var2, var3, ... struct punto pt4, pt5, pt6;
salvo che qui il tipo è una struct  In entrambi i casi è possibile inizializzare una
 La definizione di variabili può essere variabile struct:
contestuale alla dichiarazione del tipo (il tag  con valori costanti (tra parentesi graffe)
può essere omesso se non serve definire in  nel caso di variabili automatiche, anche mediante
seguito altre variabili di questo tipo) assegnazione di un’altra variabile struct dello
struct punto { pt1 stesso tipo o chiamando una funzione che
int x; x y restituisca una struct dello stesso tipo
int y;  struct punto pt7 = {12, 14};
} pt1, pt2, pt3;  3 variabili struct punto pt8 = p7;
struct punto pt9 = creapunto(5,7);
9 10

Operazioni su struct Operazioni su struct


 Per accedere ai singoli membri di una variabile  Definizione di un puntatore a struct:
di tipo struct si usa la forma: struct punto *p;
nomeVar.nomeMembro  Determinazione dell’indirizzo di una variabile di
Si noti che nomeVar è il nome della variabile, tipo struct: pt4
p = &pt4; x y
NON quello del tag 12
 Assegnazione di valore ad un membro scalare p
pt1.x = 24;
 L’assegnazione di una variabile struct ad
 L’operatore ‘.’ ha priorità maggiore
un’altra avviene mediante copia del contenuto dell’operatore di deriferimento *, per indicare
(non del puntatore), l’assegnazione è possibile il membro x della variabile di tipo struct
solo se sono dello stesso tipo puntata da p servono le parentesi:
pt1 = pt2; (*p).x = 12;

11 12

Operazioni su struct Funzioni e struct


 Invece *p.x equivale a *(p.x), cioè  Nelle funzioni, una variabile di tipo struct
l’oggetto puntato da x (se fosse un puntatore) viene passata per valore:
12  chiamata:
p funz(pt1);
x y
 definizione del parametro formale:
int funz(struct punto pt) {...}
 (*p).x viene preferibilmente scritto mediante
l’operatore freccia: p->x  Una funzione può restituire una struct
quindi si ha p->x = 12;  chiamata:
pt1 = funz(...);
 La priorità dell’operatore -> è la più alta in
dichiarazione del tipo della funzione:
assoluto, quindi ++p->x equivale a ++(p->x) 
struct punto funz(...) {...}
(incrementa x, non p)
 Determinazione dell’indirizzo di un membro:
&pt4.y in quanto & ha priorità inferiore a .
13 14

Funzioni e struct Membri di una struct


 Perché chiamante e chiamato “conoscano” la  I membri possono essere di tipo scalare, o
stessa struct, questa deve avere un’unica aggregato (vettoriale, altre struct, etc.),
dichiarazione esterna l’inizializzazione avviene come già indicato, le
 Sono considerate di tipo diverso (e quindi non parentesi graffe interne possono essere
si possono assegnare le une alle altre e tralasciate (vedere inizializzazione di matrici)
neppure sono compatibili come parametri): struct rettangolo {
 dichiarazioni identiche di struct con lo stesso tag struct punto basso_sinistra;
esterne e interne (hanno scope locale) struct punto alto_destra;
 dichiarazioni identiche di struct con lo stesso tag } rett = {{2,3},{12,9}};
interne in funzioni diverse (scope locale)
 L’accesso ai membri interni richiede
dichiarazioni di struct anche identiche ma con

l’indicazione del “percorso” da seguire:
diverso tag (interne e/o esterne) rett.alto_destra.x = 14;
 dichiarazioni di struct anche identiche ma
anonime (interne e/o esterne) variabile membro membro

15 16

Campi di bit Campi di bit


 Sono insiemi di bit che costituiscono un valore  La dimensione massima di ciascun campo è la
di tipo intero (signed o unsigned) dimensione di un int (caratteristica non
 struct cartaDaGioco { portabile, inoltre dipendente dal compilatore)
unsigned valore : 4;  I campi vengono accorpati a costituire gruppi
unsigned seme : 2; di byte delle dimensioni di un int (non è
unsigned colore : 1; specificato se da sinistra a destra o viceversa)
};  Non si può determinare il puntatore/offset di
 Il numero intero a destra di ciascun membro un campo di bit (può essere in mezzo ad un
indica per ciascun campo da quanti bit esso sia byte)
costituito
 I singoli campi si comportano come valori interi
e quindi possono comparire in espressioni,
essere assegnati, confrontati, etc.
17 18

Campi di bit Vettori di struct


 Un campo anonimo (senza nome della  Ogni elemento di un vettore di struct è una
variabile) può servire come riempitivo variabile di tipo struct: struct numParole
(padding) con quel numero di bit, ma non struct numParole { parola num
può essere utilizzato per contenere valori char parola[20];
 Un campo anonimo con dimensione 0 forza int num;
l’allineamento di memoria del membro } pn[5] = {{"ciao",2},{"hi",4}};
seguente ad al successivo int  Il vettore pn ha 5 elementi, ciascuno è una
 struct cartaDaGioco { struct numParole, i primi 2 elementi sono
unsigned valore : 4; inizializzati, i successivi sono "" e 0 (le graffe
unsigned : 5; allocati nel interne possono essere omesse)
unsigned seme : 2; primo int pn:
unsigned : 0;
unsigned colore : 1; allocato nel
ciao\0 2 hi\0 4 \0 0 \0 0 \0 0
secondo int pn[0] pn[1] pn[2] pn[3] pn[4]
};

19 20

Confronto di variabili struct Tipo union


 Per verificare se due variabili dello stesso tipo  Permette di definire una variabile che può
struct sono uguali (stesso contenuto), si contenere un solo elemento, ma di tipo diverso
deve confrontare ciascun membro  Dichiarazione simile alle strutture, ma un solo
if (pt1.x==pt2.x && pt1.y==pt2.y) membro per volta tra quelli indicati nella
dichiarazione può essere in uso
 NON si possono confrontare direttamente:  union tris {
if (pt1 == pt2)  ERRORE int x;
 NON si possono confrontare con memcmp double y;
perché i campi anonimi hanno contenuto char nome[10];
} var;
indefinito e quindi eventualmente diverso
 In questo es. la variabile var è considerata di
(ma se le variabili sono stati precedentemente tipo int se viene usata come var.x, di tipo
azzerate, ad esempio con memset o calloc, double se usata come var.y, stringa di 10
allora è possibile verificare se sono uguali) caratteri se usata come var.nome
21 22

Tipo union Operatore typedef


 Sta al programmatore mantenere memoria del  Dichiara il nome di un nuovo tipo di dato (in
tipo attuale di var e usarla coerentemente realtà un’abbreviazione) a partire da altri tipi
 L’utilizzo inizia con un’assegnazione: (scalari, aggregati, etc.)
alfa.y = 23.23;
da questo punto alfa è di tipo double e  typedef tipoEsistente nuovoTipo;
non esistono né alfa.x né alfa.nome La dichiarazione di tipo è identica alla
alfa.x = 12; definizione di una variabile, ma è preceduta
da questo punto alfa è di tipo int e non dalla clausola typedef
esistono né alfa.y né alfa.nome  Per comprendere correttamente che cosa
strcpy(alfa.nome, "ciao"); produce una dichiarazione typedef, è utile
da questo punto alfa è di tipo vettore-di-
char e non esistono né alfa.x né alfa.y pensare al tipo che avrebbe la variabile se non
 L’inizializzazione è permessa solo come ci fosse typedef e poi considerare che il
variabile del primo dei tipi indicati nella dich.: nome della variabile è in realtà il nome del
union tris alfa = 23;  inizializza x nuovo tipo

23 24

Operatore typedef Operatore typedef


 Esempi  I nomi dei tag di struct e union possono
 typedef char string[80]; essere omessi quando li si usa soltanto nella
Se non ci fosse typedef, string sarebbe una typedef (cioè la scrittura struct TAG non
variabile di tipo vettore-di-80-char; mentre grazie a compare altrove nel programma)
typedef, string è il tipo vettore-di-80-char
quindi:
 Esempi
string parola;  typedef struct rett
definisce la variabile parola di tipo string, cioè {
struct punto basso_sinistra;
di tipo char[80]
struct punto alto_destra;
 typedef char *strp; } rettangolo;
dichiara il tipo strp come puntatore a char dichiara il tipo rettangolo come struct rett
quindi: quindi:
strp par; rettangolo r = {{2,3},{12,9}};
definisce la variabile par di tipo strp, cioè char* definisce la variabile r di tipo struct rett
25 26

Operatore typedef Operatore typedef


 typedef struct  I nomi dei nuovi tipi non devono essere nomi
{ utilizzati da altri identificatori
int x;  I nomi dei tag sono scorrelati dai nomi di
int y; variabili, costanti, tipi, etc. (tecnicamente
} vpunti[10]; appartengono a name space diversi), quindi è
dichiara il tipo vpunti come vettore di 10 elementi possibile dichiarare un nome di tipo con lo
di quel tipo struct lì dichiarato (anonimo) stesso nome di un tag
vpunti vett; typedef struct rett {
definisce la variabile vett di tipo vpunti, ossia int x;
vettore-di-10-struct (la struct anonima int y;
dichiarata sopra); utilizzabile ad esempio così: } rett;
vett[0].x = 12; Si preferisce utilizzare un nome di tipo con
iniziale maiuscola (Rett) o terminante con _t
(rett_t) come d’uso nella libreria standard

27 28

Typedef e portabilità Typedef e portabilità


 L’operatore typedef viene spesso utilizzato  Ad esempio, per qualsiasi compilatore ANSI C
per “nascondere” come il compilatore realizza il tipo size_t è sempre il tipo più
internamente una certa funzionalità, fornendo appropriato (per quella combinazione
al programmatore un comportamento hardware/S.O./compilatore) per memorizzare
standard la dimensione in byte di una variabile o la
lunghezza di una stringa. Internamente un
 Questo si traduce in una migliore portabilità compilatore potrebbe usare un unsigned
del codice: indipendenza dalla piattaforma int, un altro un long; ma usando size_t
hardware, dal sistema operativo, dal non ci si deve preoccupare di questi dettagli
compilatore, etc.
 Il valore restituito richiede un cast per
l’assegnazione ad una variabile di altro tipo:
int len;
len=(int)strlen(stringa);
29 30

Typedef e funzioni Typedef e funzioni


 E’ più chiaro dichiarare esternamente (anche  Quando un tipo è dichiarato con typedef su
mediante un include) con typedef quei strutture aggregate anonime (struct e
nuovi tipi che verranno utilizzati in più funzioni union senza tag), le variabili di quel nuovo
 Ciò non è strettamente necessario in quanto tipo sono considerate dello stesso tipo
la compatibilità di tipo di due variabili viene (idealmente, nell’operazione di “smontaggio” le
determinata “smontando” la dichiarazione strutture aggregate anonime ricevono lo stesso
typedef nella sua struttura basata sui tipi tag fittizio, diverso per ogni typedef)
primitivi typedef struct {
typedef int * intp; int a;
intp p; int b;} Miastruct;
int *q; Miastruct a={0,0};
q=p;  lecito perché sono dello stesso tipo Miastruct b;
b=a;  lecito perché sono dello stesso tipo

31 32

Typedef e const Typedef e const


 Se il tipo T è dichiarato con una typedef, la  Attenzione: se invece T viene definito con una
posizione della const non è significativa #define, T non è un vero nuovo tipo e la
perché il modificatore const si applica posizione della const è significativa
all’intera typedef
 Se ad esempio T è definito come:
 Quindi le due definizioni seguenti sono #define int* T
equivalenti
const T var; la definizione const T var equivale a
T const var; const int* var;
Se ad esempio T è dichiarato come: e la definizione T const var equivale a
typedef int* T; int* const var;
entrambe le definizioni sono equivalenti a: (non è equivalente alla precedente)
int * const var;
33 34

Operatore sizeof Operatore sizeof


 Restituisce il numero di byte di cui è composto  Esempi
un tipo di dato o una variabile (scalare o sizeof(double)
aggregata) dà il numero di byte richiesti da un double
 E’ un operatore (non una funzione) e viene sizeof pt1
valutato in fase di compilazione, può essere dà il n. di byte richiesti da una struct punto
usato in una #define (ma non in una #if)  Il tipo del valore restituito è size_t
 Sintassi  Il valore restituito richiede un cast per
 sizeof(nome_di_tipo) l’assegnazione ad una variabile (ad es. di tipo
 sizeof nome_di_variabile int) in quanto size_t potrebbe essere in
Le parentesi sono necessarie se si indica il realtà un unsigned long (Warning)
nome di un tipo di dato, facoltative se si int len;
indica il nome di una variabile len=(int)sizeof(double);

35 36

Operatore sizeof Esercizi


 Definendo la variabile vett come: 1. Un file contiene, su ciascuna riga, separati da
struct x { spazi, 4 campi relativi a: nome, cognome, età,
int b; e salario (in Euro) di un certo numero di
} vett[10], s; persone (max 100). Scrivere un programma
sizeof produce i seguenti valori: che crei un vettore di 100 struct, lo riempia
 n = sizeof vett; con i dati letti dal file, lo passi ad una
dà il numero di byte richiesti da un vettore di 10
struct x funzione che lo riordini in base al cognome e
 n = sizeof vett / sizeof(struct x); quindi salvi il risultato ordinato in un secondo
dà il numero di elementi del vettore vett file mantenendo lo stesso formato del file
 n = sizeof vett / sizeof s; originale (campi separati da uno spazio).
dà il numero di elementi del vettore vett N.B. Il campo in base al quale viene riordinato
un vettore viene detto chiave.
37 38

Esercizi Esercizi
2. Si scriva un programma che dichiari il 2. Seguito
seguente tipo di dati che descrive un generico 2. double areapoli(struct poligono p);
poligono regolare calcola l’area del poligono passato
struct poligono n l2
{ A
int nlati;
4  tan πn
double lato; 3. double perimpoli(struct poligono p);
calcola il perimetro del poligono passato
};
void doppiopoli(struct poligono *pp);
Si scrivano le seguenti funzioni e le si
4.
raddoppia il lato dello stesso poligono passato
chiamino da un main di prova:
3. Come il precedente, ma si usi la typedef
1. struct poligono creapoli(void);
chiede all’utente il numero lati e la lunghezza lato,
quindi restituisce una struct poligono