Uso avanzato dei puntatori

10 aprile 2024

Strutture

Strutture in C

Il C non permette di definire Classi e oggetti, si possono comunque definire dei tipi aggiuntivi per aggregare dati:

  • Strutture: insieme di dati
  • Unioni: alternative tra tipi diversi
  • Enumerazioni: insieme di costanti

struct

Una struttura è un insieme di dati, si differenzia dagli array perché:

  • gli elementi possono essere di tipo diverso
  • gli elementi sono identificati da un nome
struct studente {
    char nome[20];
    char cognome[20];
    int matricola;
};

Inizializzazione

Si possono inizializzare le strutture quando vengono dichiarate:

struct studente s = {"Mario", "Rossi", 12345};

Si possono inizializzare meno elementi rispetto a quelli dichiarati, in questo caso gli elementi non inizializzati vengono impostati a 0.

Un modo alternativo di inizializzare:

struct studente s = {
    .nome = "Mario",
    .cognome = "Rossi",
    .matricole = 12345,
};

Accedere agli elementi

Per accedere ai singoli elementi di una struttura (detti anche membri) si usa l’operatore . (punto) dopo il nome della variabile:

struct studente s = {"Mario", "Rossi", 12345};

printf("Nome: %s\n", s.nome);
printf("Cognome: %s\n", s.cognome);
s.matricola = 54321;

Copie di strutture

Si può copiare interamente una struttura usando una variabile dello stesso tipo:

struct studente a, b = {"Matteo", "Spanio", 56789};
a = b;

Importante

Solo l’operatore = è valido tra 2 struct, gli operatori == e != NON si possono usare per vedere se due strutture sono uguali.

Dare i nomi alle strutture

Le stutture possono avere un nome, gli si può associare un tipo, oppure possono essere anonime.

Le strutture col nome, detto structure tag, si dichiarano come abbiamo già visto:

struct nome {
    type member_name;
};

Avviso

Il tipo di una variabile struct è struct nome_struct, omettere la parola struct è un errore.

Strutture anonime

Nel caso in cui un si voglia usare una struct solo in un punto specifico del codice non è necessario associarvi un nome, si può dichiarare e associare direttamente:

struct { int x; int y; } punto;
punto.x = 12;
punto.y = 18;

typedef

typedef è un operatore che permette di definire alias per i tipi:

typedef int Bool;
typedef float Euro;
typedef char* String;

Queste dichiarazioni permettono scrivere codice più chiaro.

Strutture con typedef

Dal momento che le strutture sono usate moltissimo e i programmatori sono pigri, solitamente, si preferisce omettere la parola chiave struct per riferirsi al tipo delle strutture grazie a typedef:

typedef struct {
    int x;
    int y;
} Point;
Point punto = {
    .x = 1,
    .y = 2,
};

Cast a strutture

Una volta definito il tipo con typedef, si può popolare una struttura con la stessa sintassi dell’inizializzazione, occorre fare un cast esplicito però:

typedef struct {
    int x;
    int y;
} Point;

Point s;
s = (Point) { .x = 1, .y = 2 };

struct

A prescindere da quale metodo di definizione si scelga, le strutture possono essere argomenti di funzioni e restituite da funzioni:

#include <math.h>
typedef struct point {
    int x;
    int y;
} Point;

Point somma(Point a, struct point b) {
    return (Point) { .x = a.x + b.x, .y = a.y + b.y };
}

float distanza(Point a, Point b) {
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

struct e puntatori

Si possono creare puntatori a strutture (come per qualsiasi altro tipo di dato):

struct point *p;
Point *p1;

void foo(Point* p);

struct

  • Si possono creare array di strutture:
Point punti[10];
punti[0].x = 1;
  • Inserire un array in una struttura
  • Inserire una struttura in un’altra struttura
  • Inserire unioni in una struttura
  • Inserire strutture in unioni

Esempio

Si vuole creare un sistema informatico per la gestione squadra della nazionale Italiana di calcio che giocò ai mondiali del 1982:

  1. implementare una struttura dati per memorizzare i dati di un giocatore di calcio;
  2. memorizzare l’elenco dei giocatori della squadra;
  3. ordinare i giocatori in base numero di maglia.

Memorizzare un giocatore

Ai fini della classifica è necessario memorizzare il nome della squadra e i punti ottenuti:

#define BUFFER_SIZE 128
typedef struct giocatore
{
    char nome[BUFFER_SIZE];
    char cognome[BUFFER_SIZE];
    unsigned int numero_maglia; // non esistono numeri di maglia negativi
} Giocatore;

Memorizzare la lista dei giocatori

La struct Giocatore è di fatto un tipo di dato, possiamo quindi creare un array di esami dove memorizzare la formazione:

#define NUM 22
Giocatore squadra[NUM];

Ordinare i giocatori

Per ordinare i giocatori in base al numero di maglia, bisogna introdurre un algoritmo di ordinamento:

Il più intuitivo, ma non il più efficiente, è il Bubble Sort:

Scarica l’esempio completo

Unioni

  • Le unioni sono simili alle strutture, nel senso che permettono di usare membri di tipo diverso
  • La differenza è che una struttura tiene tutti i membri in memoria, mentre una union tiene solo un membro alla volta
union {
    int i;
    double d;
} u;

struct {
    int i;
    double d;
} s;

Unioni

Si può inizializzare solo un elemento di una union:

union {
    int i;
    double d;
} u = { .i = 3 };

u.d = 5.7

Come per le strutture, posso dare nomi alle unioni tramite tag oppure con typedef.

Enumerazioni

In molti programmi si usano interi come “codici” per indicare varie cose. Un caso comune sono per esempio i codici di errore.

Spesso si usano macro o costanti, ma è più chiaro usare enumerazioni:

enum {CUORI, QUADRI, FIORI, PICCHE} s1, s2;

Ora le variabili s1 e s2 possono assumere solo uno dei quattro valori definiti.

Strutture dati

Strutture dati

Le strutture sono molto utili per definire… strutture dati.

Per esempio si possono definire array dinamici, liste concatenate, alberi, pile, code…

Array con strutture

Gli array in C sono molto limitati, infatti dobbiamo gestire la loro lunghezza a mano

Si può creare una struttura che contenga un array e la sua lunghezza:

#define MAX 1024

typedef struct intarray {
    int array[MAX];
    int length;
} IntArray;

Array con strutture

Adesso si può passare la struttura come argomento di una funzione:

void stampa_array(IntArray a) {
    for (int i = 0; i < a.length; i++) {
        printf("%d ", a.array[i]);
    }
    printf("\n");
}

Si nota subito che non abbiamo più bisogno di passare la lunghezza dell’array come argomento.

Scarica l’esempio completo