Strutture innestate e Array in linguaggio C

In linguaggio C è possibile combinare insieme strutture dati e array. Questo è uno dei punti di forza del linguaggio, che permette di realizzare strutture dati complesse in modo semplice.

Gli array possono avere come tipo di elemento una struttura dati, e le strutture dati possono contenere altri array o altre strutture dati.

In questa lezione vedremo come combinare strutture dati e array in linguaggio C.

Strutture innestate

Spesso risulta conveniente definire una struttura dati che contiene altre strutture dati. Questo tipo di strutture dati è detto struttura innestata.

Supponiamo, ad esempio, di voler realizzare un programma che gestisca i docenti e gli studenti di una scuola. Per ogni docente e per ogni studente, si vuole memorizzare il nome, il cognome e la data di nascita. Inoltre, per ogni docente si vuole memorizzare anche il numero di ore di lezione che tiene e per ogni studente si vuole memorizzare il numero di crediti accumulati.

Per memorizzare queste informazioni, si può definire una struttura dati che contiene le informazioni comuni a docenti e studenti, e due strutture dati che contengono le informazioni specifiche di docenti e studenti.

struct persona {
    char nome[20];
    char cognome[20];
    int giorno;
    int mese;
    int anno;
};

struct docente {
    struct persona dati_personali;
    int ore_lezione;
};

struct studente {
    struct persona dati_personali;
    int crediti;
};

In questo modo, se abbiamo definito una variabile di tipo struct docente o struct studente, possiamo accedere alle informazioni comuni a docenti e studenti attraverso il campo dati_personali.

struct docente d = {
    .dati_personali = {
        .nome = "Mario",
        .cognome = "Rossi",
        .giorno = 1,
        .mese = 1,
        .anno = 1970
    },
    .ore_lezione = 20
};

struct studente s = {
    .dati_personali = {
        .nome = "Luigi",
        .cognome = "Verdi",
        .giorno = 1,
        .mese = 1,
        .anno = 2002
    },
    .crediti = 180
};

printf("Nome docente: %s\n", d.dati_personali.nome);
printf("Cognome studente: %s\n", s.dati_personali.cognome);

In questo esempio, per accedere al nome del docente abbiamo applicato due volte l'operatore di accesso .: la prima volta per accedere al campo dati_personali della variabile d, e la seconda volta per accedere al campo nome della variabile d.dati_personali.

Uno dei vantaggi di utilizzare strutture dati innestate risiede nel fatto che possiamo, ad esempio, definire funzioni che lavorano sulla parte comune delle strutture. Tornando al nostro esempio, possiamo definire una funzione che stampa i dati personali di una persona, indipendentemente dal fatto che si tratti di un docente o uno studente:

void stampa_persona(struct persona p) {
    printf("Nome: %s\n", p.nome);
    printf("Cognome: %s\n", p.cognome);
    printf("Data di nascita: %d/%d/%d\n", p.giorno, p.mese, p.anno);
}

Fatto questo, possiamo invocare la funzione in questo modo:

stampa_persona(d.dati_personali);
stampa_persona(s.dati_personali);

Il secondo vantaggio sta nel fatto che possiamo copiare facilmente una struttura innestata in un'altra struttura innestata. Ad esempio, se volessimo copiare i dati personali di un docente in un'altra variabile di tipo struct persona, potremmo fare così:

struct persona p = d.dati_personali;
Definizione

Strutture Innestate

In linguaggio C una struttura dati innestata è una struttura dati definita all'interno di un'altra struttura dati.

Non vi è, nel linguaggio, un limite al numero di livelli di annidamento che è possibile utilizzare.

Per accedere ai membri di una struttura innestata, è necessario utilizzare l'operatore di accesso . per accedere ai campi della struttura esterna, e l'operatore di accesso . per accedere ai campi della struttura interna. La sintassi è la seguente:

struct interna {
    tipo membro_interno;
};

struct esterna {
    struct interna membro_struttura;
    tipo membro_esterno;
};

struct esterna variabile;

variabile.membro_struttura.membro_interno;

Array di Strutture

Una delle combinazioni più utilizzate di array e strutture è quella di utilizzare array che contengono strutture dati. In questo modo è possibile realizzare semplici basi di dati.

Ritornando all'esempio precedente, potremmo memorizzare tutte le informazioni sui docenti in un array di strutture struct docente che chiameremo docenti:

struct docente docenti[100];

Per accedere ad uno specifico docente all'interno dell'array, possiamo utilizzare l'operatore di indicizzazione []:

stampa_persona(docenti[0].dati_personali);

In questo caso abbiamo usato una combinazione di operatore di indicizzazione e operatore di accesso per accedere al campo dati_personali della variabile docenti[0].

Definizione

Array di Strutture

In linguaggio C, un'Array di Strutture è un array che contiene elementi di tipo struttura.

La sintassi per dichiarare un array di strutture è la seguente:

struct nome_struttura {
    tipo campo1;
    tipo campo2;
    ...
};

struct nome_struttura nome_array[dimensione];

Inizializzazione di Array di Strutture

Per inizializzare un array di strutture, possiamo utilizzare la sintassi che abbiamo visto per inizializzare un array multidimensionale.

Supponiamo, ad esempio, di voler memorizzare in un array di strutture un insieme di città italiane e il numero dei loro abitanti. Per ogni città, possiamo memorizzare il nome e il numero di abitanti.

struct citta {
    char nome[20];
    int abitanti;
};

A questo punto possiamo inizializzare un array di strutture struct citta che chiameremo lista_citta:

struct citta lista_citta[] = {
    {"Roma", 2873000},
    {"Milano", 1356000},
    {"Napoli", 967000},
    {"Torino", 870000},
    {"Palermo", 669000},
    {"Firenze", 383000},
    {"Bologna", 376000},
    {"Genova", 585000},
    {"Bari", 326000},
    {"Catania", 315000}
};

In realtà, avremmo potuto omettere le parentesi graffe interne, in quanto l'inizializzazione di un array di strutture è un caso particolare di inizializzazione di un array multidimensionale.

struct citta lista_citta[] = {
    "Roma", 2873000,
    "Milano", 1356000,
    "Napoli", 967000,
    "Torino", 870000,
    "Palermo", 669000,
    "Firenze", 383000,
    "Bologna", 376000,
    "Genova", 585000,
    "Bari", 326000,
    "Catania", 315000
};

Tuttavia, è sempre preferibile inserire le parentesi graffe interne, in modo da rendere più chiaro il codice.

Definizione

Inizializzazione di un Array di Strutture

In linguaggio C, l'inizializzazione di un Array di Strutture è un caso particolare di inizializzazione di un array multidimensionale.

La sintassi è la seguente:

struct nome_struttura {
    tipo campo1;
    tipo campo2;
    ...
};

struct nome_struttura nome_array[] = {
    {valore1, valore2, ...},
    {valore1, valore2, ...},
    ...
};

Inizializzatori designati e Array di Strutture in C99

Anche per gli array di strutture è possibile usare gli inizializzatori designati introdotti dallo standard C99. Questo ci permette di inizializzare solo alcuni campi della struttura, lasciando gli altri a valori di default.

Ritornando all'esempio precedente, possiamo inizializzare parte dell'array lista_citta in questo modo:

struct citta lista_citta[10] = {
    [1].nome = "Milano", [1].abitanti = 1356000,
    [2].nome = "Napoli", [2].abitanti = 967000,
    [4].nome = "Palermo",
    [6].abitanti = 376000
};
Definizione

Inizializzatori Designati in C99 e Array di Strutture

Nello standard C99 del linguaggio C è possibile utilizzare gli inizializzatori designati per inizializzare un array di strutture.

La sintassi è la seguente:

struct nome_struttura {
    tipo campo1;
    tipo campo2;
    ...
};

struct nome_struttura nome_array[] = {
    [indice1].campo1 = valore1,
    [indice1].campo2 = valore2,
    ...
    [indice2].campo1 = valore1,
    [indice2].campo2 = valore2,
    ...
};