Struct
Indice
Definizione
Il termine tipo aggregato si riferisce ai vettori e ai tipi struct:
- un vettore è un raggruppamento di variabili dello stesso tipo
- una struct è un raggruppamento di variabili anche di tipo diverso
Tipo struct
Insieme di più variabili denominate membri in genere di tipo diverso identificate da un nome comune ( tag )
In memoria i membri sono allocati contiguamente (ovvero vicine in memoria) e nello stesso ordine di dichiarazione.
Tra un membro e il successivo possono esserci (dipende dal tipo di microprocessore) spazi intermedi di allineamento della memoria non indirizzabili (quindi inutilizzabili) e dal contenuto indefinito
(Es: su ARM si vuole evitare accesso non allineato alla memoria (ovvero una variabile DEVE iniziare ad un indirizzo multiplo di 32bit), negli altri sistemi l'accesso a memoria allineata ad indirizzi non multipli di 32 bit è più lenta quindi in caso in cui la dimensione di un membro termini ad un indirizzo non multiplo di 32bit verrà inserito del padding come ottimizzazione)
https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member http://www.catb.org/esr/structure-packing/
Dichiarazione struct
// creo tipo dato struct nome_tag struct nome_tag { tipo1 nome_membro1; tipo2 nome_membro2; ... };
struct { tipo1 nome_membro1; tipo2 nome_membro2; ... } var1; // dichiarazione var1 usando struct senza tag
La dichiarazione non riserva memoria, ma crea un nuovo tipo di dato
Dichiarazioni di struct con contenuto identico ma diverso tag sono considerate tipi diversi
Una dichiarazione di struct senza tag ( anonima ) è sempre considerata avente tipo diverso da quello di ogni altra struct (con tag o anonima), anche se ha gli stessi membri.
Lo scope del nome dei membri è confinato alla sola struttura dove sono dichiarati. Altre variabili del programma e i membri di altre strutture possono avere gli stessi nomi di un membro
struct punto { int x; int y; }; struct punto p;
punto è il tag x e y sono i membri della struttura dati
Definizione di variabili struct
Riserva memoria
Ha la consueta forma:
struct nome_tag var1, var2, var3;
salvo che qui il tipo è una struct
La definizione di variabili può essere contestuale alla dichiarazione del tipo (il tag può essere omesso se non serve definire in seguito altre variabili di questo tipo)
struct punto { int x; int y; } p1, p2, p3;
Inizializzazione struct
In entrambi i casi è possibile inizializzare una variabile struct:
- con valori costanti (tra parentesi graffe)
- anche mediante assegnazione di un’altra variabile struct dello stesso tipo (da non usare se membri della struct sono puntatori)
- chiamando una funzione che restituisca una struct dello stesso tipo
// positional initializer struct punto pt7 = {12, 14}; // x vale 12, y vale 14 // designated initalizer struct punto pt10 = { .x = 100, .y = 200}; struct punto pt8 = p7; struct punto pt9 = creapunto(5,7);
Operazioni su struct
Per accedere ai singoli membri di una variabile di tipo struct si usa la forma:
variabile.nomeMembro
Si noti che variabile è il nome della variabile, NON quello del tag
Assegnazione di valore ad un membro scalare
p1.x = 24; p1 = p2;
L’assegnazione di una variabile struct ad un'altra avviene mediante copia del contenuto (non del puntatore), l’assegnazione è possibile solo se sono dello stesso tipo
Struct e funzioni
Nelle funzioni, una variabile di tipo struct viene passata per valore
funz(p1); // ha come corrispondente parametro formale void funz(struct punto p) { /* ... */ }
Una funzione può restituire una struct.
Dichiarazione del tipo della funzione e relativa chiamata:
struct punto funz() {/*...*/} p1 = funz();
Perché chiamante e chiamato "conoscano" la stessa struct, questa deve avere un'unica dichiarazione esterna
Sono considerate di tipo diverso (e quindi non si possono assegnare le une alle altre e neppure sono compatibili come parametri):
dichiarazioni identiche di struct con lo stesso tag interne in funzioni diverse (scope locale)
int main() { struct punto { int x; int y; }; f(); } void f() { struct punto { int x; int y; }; }
dichiarazioni di struct anche identiche ma con diverso tag
int main() { struct punto { int x; int y; }; struct p { int x; int y; }; // struct punto != struct p }
dichiarazioni di struct anche identiche ma anonime
int main() { struct { int x; int y; } p1; struct { int x; int y; } p2; // p1 != p2 }
Membri di struct
I membri possono essere di tipo scalare, o aggregato (vettoriale, altre struct, etc.),
L’inizializzazione avviene come già indicato, le parentesi graffe interne possono essere tralasciate (vedere inizializzazione di matrici)
struct rettangolo { struct punto basso_sinistra; struct punto alto_destra; } rett = {{2,3},{12,9}};
L’accesso ai membri interni richiede l’indicazione del “percorso” da seguire:
rett.alto_destra.x = 14;
rett è variabile, alto_destra è membro, x è membro annidato di alto_destra.
Vettori di struct
Ogni elemento di un vettore di struct è una variabile di tipo struct:
struct numParole { char parola[20]; int num; } pn[5] = {{"ciao",2},{"hi",4}};
Il vettore pn ha 5 elementi, ciascuno è una struct numParole, i primi 2 elementi sono
inizializzati, i successivi sono riempiti con "" e 0
Confronto tra struct
Per verificare se due variabili dello stesso tipo struct sono uguali (stesso contenuto), si deve confrontare ciascun membro
if (p1.x==p2.x && p1.y==p2.y) { printf("variabili uguali!"); }
NON si possono confrontare direttamente:
if (pt1 == pt2) <- ERRORE
Operatore typedef
Dichiara il nome di un nuovo tipo di dato (in realtà un’abbreviazione) a partire da altri tipi (scalari, aggregati, etc.)
typedef tipoEsistente nuovoTipo;
La dichiarazione di tipo è identica alla definizione di una variabile, ma è preceduta dalla clausola typedef
Per comprendere correttamente che cosa produce una dichiarazione typedef, è utile pensare al tipo che avrebbe la variabile se non ci fosse typedef e poi considerare che il nome della variabile è in realtà il nome del nuovo tipo
Esempi
typedef char string[80];
Se non ci fosse typedef, string sarebbe una variabile di tipo vettore-di-80-char; mentre grazie a
typedef, string è il tipo vettore-di-80-char
quindi:
string parola;
definisce la variabile parola di tipo string, cioè di tipo char[80]
typedef char *strp;
dichiara il tipo strp come puntatore a char
quindi:
strp par;
definisce la variabile par di tipo strp, quindi di tipo char*
typedef struct rett { struct punto basso_sinistra; struct punto alto_destra; } rettangolo;
dichiara il tipo rettangolo come struct rett
Quindi:
rettangolo r = {{2,3},{12,9}};
definisce la variabile r di tipo struct rett
typedef struct { int x; int y; } vpunti[10];
dichiara il tipo vpunti come vettore di 10 elementi del tipo struct lì dichiarato (anonimo)
vpunti vett; vett[0].x = 12;
definisce la variabile vett di tipo vpunti, ossia vettore-di-10-struct (la struct anonima dichiarata sopra)