Il C prevede un particolare costrutto che permette di raggruppare piu' variabili anche di tipi diversi, all'interno di un'unica entita': la struttura. Tale costrutto e' simile ai record del Pascal o del Cobol. Un esempio puo' essere una struttura 'anagrafica' che contiene tutte le informazioni anagrafiche appunto, di un cliente. La sintassi e' la seguente:
nell'esempio qui sopra e' stata dichiarata una struttura di nome 'punto' che contiene le coordinate x e y. La parola chiave struct e' obbligatoria ed e' seguita da un nome opzionale detto identificatore che identifica il tipo di struttura. Le variabili specificate all'interno della struttura vengono definite membri. Una dichiarazione struct definisce un tipo, ma non alloca spazio in memoria. E' possibile tuttavia allocare definire una struttura ed allocare un oggetto in memoria del tipo appena definito:
dove 'miopunto' e' una variabile di tipo 'punto' e punto e' una struttura. E' possibile istanziare una variabile di tipo punto anche dopo aver dichiarato la struttura:
Occorre fare attenzione al punto e virgola (;) che termina la dichiarazione di struttura. La prima espressione dichiara un tipo di struttura, mentre la seconda espressione definisce una variabile che ha le caratteristiche della struttura dichiarata prima. E' possibile inizializzare una struttura mediante un assegnamento simile a quello utilizzato nei vettori:
dove all'interno delle parentesi graffe e' presente una lista di inizializzatori. Per far riferimento ad un membro di una struttura si utilizza il simbolo '.' attraverso un costrutto di questo tipo:
Le strutture possono essere nidificate, percio', data la struttura punto dichiarata prima, e' possibile ad esempio dichiarare una seconda struttura:
Quando si dichiara una struttura, si definisce una tipologia di variabile composta (cioe' derivata dai tipi primitivi). La struttura rettangolo ad esempio, definisce la tipologia di variabili 'rettangolo', percio' e' possibile dichiarare delle variabili di questo tipo appena appena creato:
mio_rect.p1.x, mio_rect.p1.y, mio_rect.p2.x e mio_rect.p2.y, corrispondono ai 4 punti del rettangolo mostrato sopra.
Una struttura e' una variabile composta e derivata dai tipi primitivi del C, ma puo' essere gestita come qualsiasi variabile di tipo primitivo. Percio' una struttura puo' essere assegnata, copiata, passata ad una funzione, indirizzata mediante l'operatore & e cosi' via. E' anche possibile allocare vettori di strutture e puntatori a strutture. E' possibile passare ad una funzione un'intera struttura, membri della struttura od un puntatore ad essa:
la funzione creapunto riceve in input due int e ritorna in output una struttura di tipo punto (temp). Le variabili di tipo int passate in input hanno lo stesso nome dei membri della struttura (x e y): cio' non crea confusioni in quanto sono due variabili distinte. Infatti x e' una variabile di tipo int mentre temp.x e' il membro 'x' della stuttura temp di tipo punto (ovviamente anche questo membro a sua volta e' una variabile di tipo int). La funzione sommapunti invece, riceve in input le strutture p1 e p2, effettua dei calcoli utilizzandone i membri ed infine ritorna in output la struttura p1. E' possibile passare ad una funzione un puntatore ad una struttura. Cio' puo' essere fatto dichiarando un puntatore al quale viene assegnato l'indirizzo di una struttura:
la funzione printf() riceve in input i membri della struttura punto, pero', nel passaggio di questi parametri, non si utilizza la struttura punto ma bensi' il puntatore ad essa. Infatti dopo l'assegnamento p_punto = &puntoqualsiasi, p_punto punta alla struttura puntoqualsiasi, percio' *p_punto e' la struttura puntata. Quindi e' possibile scrivere indifferentemente puntoqualsiasi.x oppure (*p_punto).x, in quanto quest'ultima espressione rappresenta il membro x della struttura puntoqualsiasi. Nell'espressione (*p_punto).x le parentesi sono necessarie in quanto l'operatore di struttura '.' ha una precedenza superiore all'operatore di deriferimento '*': senza di esse l'espressione *p_punto.x equivale all'espressione *(p_punto.x) che non ha alcun significato in quanto (p_punto.x) non e' un puntatore ma bensi' un membro della struttura punto (piu' esattamente una variabile di tipo int). Esiste una notazione alternativa per rappresentare i puntatori alle strutture:
l'operatore '->' ha una precedenza superiore a quasi tutti gli altri operatori: piu' esattamente ha la stessa precedenza degli operatori '()', '[]' e '.' ed e' associativo da sinistra a destra (come gli altri 3 operatori). Data la precedenza cosi' alta, l'espressione:
incrementa il membro x e non il puntatore alla struttura. Infatti tale espressione equivale a (p_punto->x). Per incrementare il puntatore alla struttura e non il suo membro occorre percio' scrivere ( p_punto)->x.
Il C consente di utilizzare vettori composti da variabili di qualsiasi tipo, comprese le strutture. E possibile quindi dichiarare una struttura ed un vettore di strutture del tipo appena dichiarato:
struttvett e' un vettore di 10 strutture di tipo miastruttura. Ogni elemento di tale vettore e' una struttura. Le due forme appena viste sono equivalenti: nella prima viene dichiarata una struttura miastruttura e successivamente viene dichiarato un vettore di 10 strutture; nella seconda vengono dichiarati sia la struttura che il vettore contemporaneamente. E' possibile inizializzare il vettore di strutture in questo modo:
come al solito l'aritmetica dei puntatori e' utilizzabile anche in questo contesto, pertanto, l'incremento od il decremento di un puntatore ad una struttura e' possibile. Incrementando il puntatore ad una struttura, viene tenuto conto delle dimensioni della struttura percio' il puntatore viene incrementato di una quantita' tale da puntare alla struttura successiva. Attenzione a non cascare nell'errore di ritenere tale quantita' pari alla somma delle dimensioni dei membri della struttura. Infatti a causa dell'allineamento degli oggetti in memoria, qualsiasi oggetto potrebbe occupare una quantita' di caratteri minima a prescindere dalla sua dimensione. Ad esempio, se un int, all'interno di una particolare macchina ha una dimensione di 4 byte, in realta' tale oggetto potrebbe occupare 8 byte. Percio' una struttura composta da un char e da un int, che in teoria ha una dimensione di 5 byte (1 4) in realta' in memoria potrebbe occupare 8 byte. Per scoprire le dimensioni di un oggetto esiste l'operatore sizeof. L'operatore sizeof ritorna la dimensione di un oggetto in memoria all'interno della macchina che si sta utilizzando.
Puo' essere conveniente o necessario usare campi di bit, ad esempio per utilizzare una serie di switch senza occupare troppo spazio in memoria. Cosi' lo switch TROVATO puo' essere un bit impostato ad 1 se e' stata trovata una stringa 0 se non e' stata trovata, mentre lo switch FINEFILE puo' valere 1 o 0 se e' stata raggiunta o meno la fine di un file. Nella programmazione in generale, per convenzione 0 corrisponde a falso e 1 a vero, percio' e' possibile disporre di piu' switch delle dimensioni di un bit per gestire varie condizioni. Un metodo classico per impostare i campi di bit e' gia' stato visto precedentemente, e comporta l'uso delle maschere di bit. Ad esempio:
nell'esempio appena visto, miei_switches comprende una serie switch da un bit ciascuno: mediante l'operatore OR ('|') i bit corrispondenti agli switch EXTERNAL e STATIC vengono impostati ad 1 (secondo e terzo bit di miei_switch). E' possibile pero' utilizzare le strutture per manipolare i bit direttamente, senza cioe' l'uso di operatori bitwise:
gli oggetti definiti in questa struttura compongono il bit-field, cioe' campo di bit. La struttura cosi' definita rappresenta quindi una sequenza di bit adiacenti all'interno di una singola unita' di memoria. Viene cosi' definita la variabile miei_switch che contiene 3 campi da un bit ciascuno, in quanto il valore che segue i due punti definisce l'ampiezza del bit-field (in questo caso il valore e' 1, quindi il campo rappresenta un bit). Per accedere ai singoli campi della struttura (cioe' in questo caso ai singoli bit) si usa una sintassi analoga a quella usata per membri delle strutture:
l'istruzione appena vista imposta ad 1 i bit corrispondenti ai campi is_extern e is_static (secondo e terzo bit di miei_switch).
un costrutto dalla sintassi simile alle strutture ma profondamente diverso come significato e' la union. Una union e' una variabile che rappresenta in momenti diversi oggetti di tipo e dimensioni diverse. Un costrutto simile e' il record variante del Pascal o alla clausola redefines del Cobol:
nell'esempio precedente variabilona e' una variabile sufficientemente grande da contenere la variabile di dimensioni maggiori tra le tre dichiarate. Per accedere ai membri di una union si utilizza una sintassi analoga ai membri delle strutture:
Inizio della guida
I puntatori
Indice
Input ed Output
Copyright (c) 2002-2003 Maurizio Silvestri
Le strutture
30.1 Le strutture: generalita'
struct nome-opzionale { var1; var2; ...} var-opzionale1, var-opzionale2, ...;
esempio:
struct punto
{
int x;
int y;
}
struct punto
{
int x;
int y;
} miopunto;
struct punto
{
int x;
int y;
};
struct punto miopunto; /* concettualmente simile ad una dichiarazione come: int mia_variabile; */
struct punto miopunto = {100, 200};
nome-struttura.membro
esempio:
miopunto.x = 100;
miopunto.y = 200;
struct rettangolo
{
struct punto p1;
struct punto p2;
};
struct rettangolo mio_rect;
e quindi:
Y
^
|
|
| p2
| -------------
| | |
| | |
| -------------
| p1
----- ----------------------------> X
|
|
30.2 Funzioni e strutture
struct punto creapunto (int x, int y)
{
struct punto temp;
temp.x = x;
temp.y = y;
return temp;
}
oppure:
struct punto sommapunti (struct punto p1, struct punto p2)
{
p1.x = p1.x p2.x;
p1.y = p1.y p2.y;
return p1;
}
struct punto puntoqualsiasi, *p_punto; /* dichiara un puntatore alla struttura 'punto' */
p_punto = &puntoqualsiasi; /* assegna al puntatore l'indirizzo della struttura puntoqualsiasi */
printf ("le coordinate di puntoqualsiasi sono: (%d, %d)\n", (*p_punto).x, (*p_punto).y);
puntatore->membro
che equivale a:
(*puntatore).membro
quindi l'esempio precedente poteva anche essere scritto cosi':
printf ("le coordinate di puntoqualsiasi sono: (%d, %d)\n", p_punto->.x, p_punto->.y);
p_punto->x;
30.3 Vettori di strutture
struct miastruttura
{
char var1;
int var2;
};
struct miastruttura struttvett[10];
oppure:
struct miastruttura
{
char var1;
int var2;
} struttvett[10];
struct miastrutt
{
int intero;
char carattere;
} vettore[] = {
0 , 'a',
1 , 'b',
2 , 'c',
....
....
9 , 'i'
};
30.4 Strutture di bit
#define KEYWORD 01
#define EXTERNAL 02
#define STATIC 04
miei_switch |= EXTERNAL | STATIC;
struct
{
unsigned int is_keyword: 1;
unsigned int is_extern: 1;
unsigned int is_static: 1;
} miei_switch;
miei_switch.is_extern = miei_switch.is_static = 1;
30.5 Le union
union mia_union
{
int intero;
float mio_float;
char * stringa;
} variabilona;
nome_union.nome_membro
oppure:
puntatore_union->nome_membro