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)

Validate