Nei titoli e nei testi troverete qualche rimando cinematografico (ebbene si, sono un cinefilo). Se non vi interessano fate finta di non vederli, già che non sono fondamentali per la comprensione dei post...

Di questo blog ho mandato avanti, fino a Settembre 2018, anche una versione in Spagnolo. Potete trovarla su El arte de la programación en C. Buona lettura.

mercoledì 25 marzo 2026

The (Duel)Lists
come usare le Linked List in C - pt.2

Armand d'Hubert: [rivolgendosi a Gabriel Feraud] Sono stato ai tuoi ordini per 15 anni... Secondo le regole dei duelli adesso la tua vita mi appartiene. Dichiarerò che sei morto. Se avrai mai a che fare con me, lo farai da uomo morto. Mi sono piegato alla tua nozione di onore per troppo tempo. Adesso tu ti piegherai alla mia.

(...una premessa: questo post (parte 2 di 3) è un remake di un mio vecchio post. Ma, anche se tratta lo stesso argomento, amplia e perfeziona un po' il discorso è mi è sembrato il caso di riproporlo. Leggete e mi direte...)

Allora: dove eravamo rimasti? Ah, si: le linked list. Nell'ultimo articolo avevo spiegato l'importanza di questi oggetti nel nostro linguaggio preferito e avevo anche fornito un esempio molto elementare (ma funzionale!). È giunto ora il momento di estendere un po' l'esempio (l'avevo promesso, e "ogni promessa è debito"). Leggete attentamente l'articolo perché l'argomento è moderatamente intricato e intrigante e, mi raccomando, non fate come il dottor Jacquin di The Duellists (I duellanti) di Ridley Scott: se qualcosa non vi è chiaro provate e riprovate fino a trovare il bandolo della matassa, non vi arrendete. Come avrete già intuito, anche in questo articolo parleremo di linked list!

...Ci risiamo...
La promessa da mantenere consisteva in aggiungere, all'esempio fornito, qualche funzione di utilità e, soprattutto, permettere di liberare la memoria occupata con malloc(3) usando le opportune free(3). Alla fine ho pensato di aggiungere quattro funzioni:
  • findNode() - cerca un node specifico. In questo esempio il node è identificato dal campo "data" che funge anche da identificativo (vedi note più avanti).
  • delNode() - cancella un node specifico (e libera la memoria). In questo esempio il node è identificato dal campo "data" che funge anche da identificativo (vedi note più avanti).
  • cleanList() - cancella l'intera lista (e libera la memoria).
  • printList() - mostra il contenuto dell'intera lista.

Ebbene si, credo che sia giunto il momento di mostrare la nuova versione del programma. Vai col codice!

// linklist.c - un programma di esempio sulle linked list
#include <stdlib.h>
#include <stdio.h>

// node di una linked list singola con campo dati
typedef struct snode {
int data;
struct snode *next;
} node_t;

// prototipi locali
void addNode(node_t **head, int data);
void appendNode(node_t **head, int data);
void findNode(node_t **head, int data);
void delNode(node_t **head, int data);
void cleanList(node_t **head);
void printList(node_t **head);

// funzione main
int main()
{
// init lista vuota e inserisceo con addNode() 5 nodi con data = indice del loop
node_t *head = NULL;
for (int i = 0; i < 5; i++)
addNode(&head, i);

// mostro la lista
printList(&head);

// cerco un node: quello con data == 3
findNode(&head, 3);

// cancello un node: quello con data == 3
delNode(&head, 3);

// cerco il node cancellato (quello con data == 3)
findNode(&head, 3);

// mostro di nuovo la lista: deve mancare il node con data == 3
printList(&head);

// cancello l'intera lista
cleanList(&head);

// mostro di nuovo la lista: deve essere vuota
printList(&head);

return 0;
}

// addNode - alloca in testa a una lista un node con dati e pointer al prossimo elemento
void addNode(node_t **head, int data)
{
// alloco un nuovo node
node_t *node = malloc(sizeof(node_t));
node->data = data;
node->next = *head;

// assegno la head lista al nuovo node
*head = node;
}

// appendNode - alloca in fondo a una lista un node con dati e pointer al prossimo elemento
void appendNode(node_t **head, int data)
{
// alloca un nuovo node
node_t *node = malloc(sizeof(node_t));
node->data = data;
node->next = NULL;

// appende il nuovo node
node_t *current = *head;
if (current == NULL) {
// caso speciale per lista vuota: assegna head lista al nuovo node
*head = node;
}
else {
// scorro la lista per trovare l'ultimo node
while (current->next != NULL)
current = current->next;

// assegno all'ultimo node il nuovo node
current->next = node;
}
}

// findNode - trova un node nella lista
void findNode(node_t **head, int data)
{
// scorro la lista
node_t *current = *head;
while (current) {
// cerco il node con il <data> corrispondente
if (current->data == data) {
// node trovato!
printf("node trovato: data=%d (myhead=%p next=%p)\n",
current->data, (void *)current, (void *)current->next);
return;
}

// passo al priossimo node
current = current->next;
}

// node non trovato!
printf("node con data=%d non trovato!\n\n", data);
return;
}

// delNode - rimuove un node dalla lista
void delNode(node_t **head, int data)
{
// scorro la lista
node_t *current = *head;
node_t *previous = NULL;
while (current) {
// cerco il node con il <data> corrispondente
if (current->data == data) {
// modifico il riferimento <next> per eliminare dall'elenco il node trovato
if (previous)
previous->next = current->next;
else
*head = current->next;

// cancello il node e ritorno
printf("%s: cancello data=%d (current=%p next=%p)\n",
__func__, current->data, (void *)current, (void *)current->next);
free(current);
return;
}

// passo al prossimo node
previous = current;
current = current->next;
}
}

// cleanList - reset completo della lista
void cleanList(node_t **head)
{
// scorro la lista
node_t *current = *head;
node_t *next;
while (current) {
// salvo il node successivo prima di liberare quello corrente
next = current->next;

// cancello il node
printf("%s: cancello data=%d (current=%p next=%p)\n",
__func__, current->data, (void *)current, (void *)current->next);
free(current);

// mi sposto sul node successivo
current = next;
}

// imposto la head lista a NULL per indicare che la lista è vuota
*head = NULL;
}

// printList - mostra la lista
void printList(node_t **head)
{
// scorro la lista e stampo i valori
node_t *current = *head;
printf("%s: lista in %p - elementi contenuti:\n", __func__, (void *)current);
while (current) {
printf("data=%d (current=%p next=%p)\n",
current->data, (void *)current, (void *)current->next);
current = current->next;
}

printf("\n");
}

Come avrete notato è molto simile a quello dell'ultimo articolo ma, anche grazie alla nuova funzione printList(), il codice è un po' più lineare e leggibile. Credo che grazie ai commenti esagerati sia tutto molto chiaro, ma qualche nota da aggiungere c'è: questo è un esempio molto semplificato, quindi non è il caso di chiedersi a che serve una linked list con un solo dato (che è, oltretutto, un semplice int): evidentemente una linked list può contenere (oltre all'indispensabile campo "next") quanti altri campi semplici si vogliono, oppure un solo campo data che può essere, a sua volta, una struttura complessa, oppure più strutture complesse, ecc.

Però la architettura del codice rimarrá la stessa, semplicemente sarà un po' più complicato aggiungere e cancellare elementi nel caso che ci siano altre malloc(3)/free(3) da fare, ma niente di cui preoccuparsi. E anche la findNode() e la delNode() potrebbero cercare un semplice campo identificativo oppure cercare una struttura complessa: il meccanismo si può semplificare o complicare a piacere, ma alla fin fine l'architettura di base è quella che vi ho appena mostrato.

Ah, un'ultima cosa, e scusatemi se mi ripeto: quello sopra è un esempio molto semplificato ma, ovviamente, se si vorrà usare come codice di produzione bisognerà aggiungere gli opportuni controlli di esecuzione (specialmente sull'esito delle malloc(3)) e gli opportuni trattamenti degli errori del caso. Non dimenticatevene mai!

Cosa manca per finire l'articolo? Ah, si, compilare ed eseguire. Vediamo un po:

aldo@Linux $ gcc -Wall -Wextra -pedantic linklist.c -o linklist
aldo@Linux $ ./linklist
printList: lista in 0x56a7786e6320 - elementi contenuti:
data=4 (current=0x56a7786e6320 next=0x56a7786e6300)
data=3 (current=0x56a7786e6300 next=0x56a7786e62e0)
data=2 (current=0x56a7786e62e0 next=0x56a7786e62c0)
data=1 (current=0x56a7786e62c0 next=0x56a7786e62a0)
data=0 (current=0x56a7786e62a0 next=(nil))

node trovato: data=3 (myhead=0x56a7786e6300 next=0x56a7786e62e0)
delNode: cancello data=3 (current=0x56a7786e6300 next=0x56a7786e62e0)
node con data=3 non trovato!

printList: lista in 0x56a7786e6320 - elementi contenuti:
data=4 (current=0x56a7786e6320 next=0x56a7786e62e0)
data=2 (current=0x56a7786e62e0 next=0x56a7786e62c0)
data=1 (current=0x56a7786e62c0 next=0x56a7786e62a0)
data=0 (current=0x56a7786e62a0 next=(nil))

cleanList: cancello data=4 (current=0x56a7786e6320 next=0x56a7786e62e0)
cleanList: cancello data=2 (current=0x56a7786e62e0 next=0x56a7786e62c0)
cleanList: cancello data=1 (current=0x56a7786e62c0 next=0x56a7786e62a0)
cleanList: cancello data=0 (current=0x56a7786e62a0 next=(nil))
printList: lista in (nil) - elementi contenuti:

Beh, i risultati parlano chiaro, no? Corrispondono ai passi indicati nel codice:

  1. Creo una lista di cinque elementi e stampo il contenuto (indirizzi di memoria inclusi! Per chi ha dubbi sul funzionamento delle linked list...)
  2. Cerco un node (in questo caso quello con data == 3) e mostro cosa ho trovato.
  3. Cancello un node (in questo caso quello con data == 3).
  4. Cerco un node (in questo caso quello con data == 3) e mostro che non l'ho trovato (certo che se lo trovavo era un problema...).
  5. Mostro di nuovo la lista: deve mancare il node con data == 3 (speriamo...)
  6. Cancello l'intera lista e stampo il contenuto: non deve apparire nulla (di nuovo: speriamo...).

Funziona!

Quando ho cominciato a scrivere lo scorso articolo pensavo a una cosa semplice semplice in una sola parte e via... poi mi è sembrato il caso di aggiungere questa seconda parte e... ma si, prolungherò ulteriormente la descrizione: nel prossimo articolo parlerò di doubly linked list! Così mi tolgo il pensiero e non dovrò ritornare sull'argomento chissà quando. Restate sintonizzati!

Ciao, e al prossimo post!

venerdì 27 febbraio 2026

The (Duel)Lists
come usare le Linked List in C - pt.1

Armand d'Hubert: [rivolgendosi a Gabriel Feraud] Sono stato ai tuoi ordini per 15 anni... Secondo le regole dei duelli adesso la tua vita mi appartiene. Dichiarerò che sei morto. Se avrai mai a che fare con me, lo farai da uomo morto. Mi sono piegato alla tua nozione di onore per troppo tempo. Adesso tu ti piegherai alla mia.

(...una premessa: questo post (parte 1 di 3) è un remake di un mio vecchio post. Ma, anche se tratta lo stesso argomento, amplia e perfeziona un po' il discorso è mi è sembrato il caso di riproporlo. Leggete e mi direte...)

The Duellists (I duellanti) è uno dei capolavori di Ridley Scott, ed è il suo film d'esordio (e che esordio!); racconta la storia di due ufficiali francesi (interpretati da Keith Carradine e Harvey Keitel) che si affrontano a duello in vari scenari europei durante le campagne napoleoniche, per ben quindici anni. È un film imperdibile! Ah, e visto che siamo in tema: vorrei ricordarvi che se qualcuno parla male delle linked list lo sfido a duello, quindi state attenti... Come avrete già intuito, in questo articolo parleremo di linked list!

...Cosa dicevi delle linked list?...

Da queste parti avevamo già affrontato (di striscio) il tema delle linked list, in due vecchi articoli, qui e qui. Ricordo anche che una volta giustificai l'argomento così:

"Per chi non le avesse mai usate, le linked-list non sono una frivolezza: sono una delle costruzioni più potenti della programmazione in generale (e del C in particolare). Chi lavora intensamente in C su progetti grandi e professionali finirà, prima o poi, con usarle: un motivo in più, quindi, per familiarizzare con la allocazione dinamica della memoria."

Ok, credo che la maniera migliore di presentare l'argomento sia un esempietto semplice semplice (ma, come sempre: funzionante!). Vai col codice!

// linklist.c - un programma di esempio sulle linked list
#include<stdlib.h>
#include<stdio.h>

// nodo di una linked list singola con campo dati
typedef struct snode {
int data;
struct snode *next;
} node_t;

// prototipi locali
void addNode(node_t **head, int data);
void appendNode(node_t **head, int data);

// funzione main
int main()
{
// init lista vuota 1 e inserisce con addNode() 3 nodi con data = indice del loop
node_t *head1 = NULL;
for (int i = 0; i < 3; i++)
addNode(&head1, i);

// scorre la lista e stampa i valori
node_t *myhead1 = head1;
printf("myhead1=%p - metodo ADD\n", (void *)myhead1);
while (myhead1) {
printf("data=%d (myhead1=%p next=%p)\n",
myhead1->data, (void *)myhead1, (void *)myhead1->next);
myhead1 = myhead1->next;
}

// init lista vuota 2 inserisce con appendNode() 3 nodi con data = indice del loop
node_t *head2 = NULL;
for (int i = 0; i < 3; i++)
appendNode(&head2, i);

// scorre la lista e stampa i valori
node_t *myhead2 = head2;
printf("myhead2=%p - metodo APPEND\n", (void *)myhead2);
while (myhead2) {
printf("data=%d (myhead2=%p next=%p)\n",
myhead2->data, (void *)myhead2, (void *)myhead2->next);
myhead2 = myhead2->next;
}

return 0;
}

// addNode - alloca in testa a una lista un node con dati e pointer al prossimo elemento
void addNode(node_t **head, int data)
{
// alloca un nuovo node
node_t *node = malloc(sizeof(node_t));
node->data = data;
node->next = *head;

// assegna head lista al nuovo node
*head = node;
}

// appendNode - alloca in fondo a una lista un node con dati e pointer al prossimo elemento
void appendNode(node_t **head, int data)
{
// alloca un nuovo node
node_t *node = malloc(sizeof(node_t));
node->data = data;
node->next = NULL;

// appende il nuovo node
node_t *current = *head;
if (current == NULL) {
// caso speciale per lista vuota: assegna head lista al nuovo node
*head = node;
}
else {
// scorre la lista per trovare l'ultimo node
while (current->next != NULL)
current = current->next;

// assegna all'ultimo node il nuovo node
current->next = node;
}
}

Cosa dire? Più elementare di così! Eppure funziona... Come si evince dagli abbondanti commenti per fare una linked list basta creare un punto di inizio (un "head") e aggiungere nodi con una semplicissima funzione che ho chiamato (in modo veramente originale) addNode(). Il segreto sta nella struttura del nodo, che deve sempre contenere un pointer al nodo successivo (ma va? Sarà mica per questo che si chiamano linked list?). Dato che c'ero ho anche aggiunto una funzione per appendere nodi, che è ugualmente utile. Ovviamente l'esempio fornito è abbastanza limitato: mancano le funzioni per cancellare i nodi, oppure per gestire la lista in modo doppio (double-linked-list), ecc. Ma sulla (solida) base fornita è possibile costruire, in modo relativamente semplice, tutte le estensioni che vogliamo.

Nel main() ho aggiunto un po' di tracce di log, che ci aiutano, durante l'esecuzione, a capire cosa succede quando aggiungiamo un nodo e quando, in alternativa, lo appendiamo. Il risultato finale è lo stesso (nell'esempio è una lista con tre nodi), ma la disposizione cambia: nel caso append  abbiamo una disposizione più logica dei nodi: la lista cresce in avanti, e ogni nuovo nodo è l'ultimo. Nel caso insert, invece, è la testa della lista che cambia ad ogni inserzione, e quindi, quando leggiamo i dati, li troviamo invertiti rispetto all'ordine di inserimento.

Entrambe le liste hanno usi validi (ad esempio una va meglio per creare code FIFO e l'altra va meglio per creare code LIFO) e, per applicazioni più sofisticate dove bisogna aggiungere/leggere/cancellare nodi in posizioni determinate si può decidere (quasi) indistintamente per una struttura o l'altra. Io normalmente uso il metodo append, che mi sembra più logico, anche se, lo riconosco, la funzione di append è un po' più complicata e meno immediata di quella di insert. Però la lista ordinata in avanti mi sembra più coerente e facile da maneggiare.

Compilando (con gli opportuni flag) ed eseguendo, il programma ci mostrerà questo log:

aldo@Linux $ gcc -Wall -Wextra -pedantic linklist.c -o linklist
aldo@Linux $ ./linklist
myhead1=0x5a2fd71412e0 - metodo ADD
data=2 (myhead1=0x5a2fd71412e0 next=0x5a2fd71412c0)
data=1 (myhead1=0x5a2fd71412c0 next=0x5a2fd71412a0)
data=0 (myhead1=0x5a2fd71412a0 next=(nil))
myhead2=0x5a2fd7141710 - metodo APPEND
data=0 (myhead2=0x5a2fd7141710 next=0x5a2fd7141730)
data=1 (myhead2=0x5a2fd7141730 next=0x5a2fd7141750)
data=2 (myhead2=0x5a2fd7141750 next=(nil))

che mi sembra inutile spiegare (parla da solo!). Come già accennato sopra, il codice presentato è solo un "buon inizio": in una applicazione reale bisognerà aggiungere un po' di funzioni accessorie per cercare, inserire e cancellare nodi. E, visto che usiamo delle malloc(3), bisognerà anche aggiungere, quando servono, le corrispondenti free(3)... Ma anzi, crepi l'avarizia! Farò una seconda parte dell'articolo aggiungendo quello che manca, ma sempre in maniera semplificata e ben comprensibile, eh!

Ok, ci sentiremo prossimamente con la seconda parte dell'articolo e, come sempre, vi ricordo di non trattenere il respiro nell'attesa (può nuocere gravemente alla salute...).

Ciao, e al prossimo post! 

martedì 27 gennaio 2026

Warning Peaks
perché i warning sono importanti in C - pt.2

Dale Cooper: Harry, ti dirò un piccolo segreto. Ogni giorno, una volta al giorno, fatti un regalo. Non pianificarlo, non aspettarlo... fallo solo succedere.

Ok, è già ora di tornare sul pezzo. Dove eravamo rimasti? Ah, si: nell'ultimo articolo avevo parlato dell'importanza, spesso sottovalutata, dei warning, e avevo anche mostrato qualche (spero) interessante esempio. Avevo poi concluso dicendo che, nella seconda parte avremmo visto una panoramica dei flag consigliati per scrivere una applicazione impeccabile ("a prova di bomba"). E allora, come diceva (qui sopra) il buon Dale Cooper nel mitico Twin Peaks, oggi tocca auto-regalarsi qualcosa di molto utile... come avrete già intuito, anche in questo articolo parleremo di warning!

...Harry, regalati un warning e risolvilo. La tua applicazione ti ringrazierà...

Avevo già anticipato nello scorso articolo che alcuni warning appaiono di base, mente altri appaiono usando il flag di compilazione -Wall. È quindi venuto il momento di mostrare una lista degli altri flag che si possono usare, anche se vedremo solo i più significativi per lo scopo che ci siamo prefissi. I flag disponibili con GCC sono tantissimi, e alcuni sono dei "flag di gruppo" (come il -Wall) che raggruppano molte opzioni in una botta sola. Vedremo, quindi, i flag di gruppo più utili e anche alcuni flag semplici da usare separatamente. Vai con la lista!

Flag di gruppo

  • -Wall: abilita i warning più comunemente utili, tra cui variabili non inizializzate, parametri inutilizzati e costrutti sospetti. Include ben 81 flag semplici che si possono usare separatamente (-Waddress, -Warray-compare -Warray-parameter=2, ecc.), ed è il flag di gruppo "principe", quello assolutamente da usare.
  • -Wextra: aggiunge warning più completi rispetto a -Wall, come parametri di funzione inutilizzati, variabili oscurate e dichiarazioni di funzione implicite. Include ben 29 flag semplici che si possono usare separatamente (-Walloc-size, -Wcalloc-transposed-args, -Wcast-function-type, ecc.), ed è il flag di gruppo "principe" n.2, da usare sempre in coppia con -Wall (o anche da solo, ma in coppia è meglio... scusate il doppio senso, ah ah ah).
  • -Wformat=2: combina i flag semplici -Wformat, -Wformat-nonliteral, -Wformat-security e -Wformat-y2k per rilevare l'uso non sicuro delle stringhe di formato. È un flag utile ma solo per usi avanzati e restrittivi, normalmente è sufficiente usare il -Wformat=1 che è incluso nel -Wall.

Flag semplici

  • -pedantic (oppure -Wpedantic): impone la stretta conformità agli standard ISO C/C++, segnalando le estensioni non standard e il codice non conforme. È un flag altamente raccomandabile.
  • -Werror: tratta tutti i warning come errori, impedendo la compilazione fino alla risoluzione dei problemi, ideale per il codice di produzione. È un flag utile ma non strettamente indispensabile; comunque è importante sapere che è disponibile.
  • -Wshadow: avvisa quando una variabile locale oscura una variabile da uno scope esterno. È un flag altamente raccomandabile.
  • -Wnull-dereference: avvisa in merito a percorsi che potrebbero portare a referenze di puntatori nulli. È un flag utile ma solo per usi avanzati e restrittivi.
  • -Wcast-align=strict: rileva conversioni di pointer non sicure che potrebbero causare problemi di disallineamento su determinate architetture. È un flag utile ma solo per usi avanzati e restrittivi.

Che vi sembra la lista? È, ovviamente, una selezione molto ponderata, ma abbastanza completa; e comunque, se volete conoscere tutti il flag disponibili, potete consultarli qui (ma mettetevi comodi, è una lista lunghissima!).

A questo punto, magari, vi può anche interessare cosa usa, normalmente, il vostro C-blogger preferito (io, no? ah ah ah) quando scrive codice di produzione... ecco la linea!

gcc -Wall -Wextra -pedantic -Wshadow

a volte aggiungo, poi, anche alcuni flag semplici  in base alle esigenze del momento, ma normalmente la linea di compilazione che uso è questa, e permette di produrre applicazioni molto affidabili, ve lo garantisco! Provare per credere...

E direi di finire con una altro esempietto, perché un articolo di programmazione senza un esempio reale è un articolo incompiuto... Vediamo un po': il codice seguente ha un problema, ma compilando senza warning, o anche usando -Wall non segnala niente (!). Ci vorrà mica il -Wextra? Vai col codice!

// typelimits.c
#include <stdio.h>

// prototipi locali
int isGreaterThan200(char c);

// funzione main
int main()
{
int i = 250;
printf("arg = %d --> %s\n",
i, isGreaterThan200(i) ? "è maggiore di 200" : "non è maggiore di 200");
return 0;
}

// isGreaterThan200 - valuta se l'argomento è più grande di 200
int isGreaterThan200(char arg)
{
if (arg > 200) // 200 è troppo grande per un char: la comparazione è sempre falsa
return 1;
else
return 0;
}

compiliamo con -Wall ed eseguiamo:

aldo@Linux $ gcc -Wall typelimits.c -o typelimits
aldo@Linux $ ./typelimits
arg = 250 --> non è maggiore di 200

Ma che strano, è un programma molto semplice ed eppure funziona così male! E il compilatore non ci segnala nulla! Proviamo allora a compilare con -Wextra:

aldo@Linux $ gcc -Wextra typelimits.c -o typelimits
typelimits.c: In function ‘isGreaterThan200’:
typelimits.c:20:13: warning: comparison is always false due to limited range of data type [-Wtype-limits]
20 | if (arg > 200) // 200 è troppo grande per un char: la comparazione è sempre falsa
| ^
aldo@Linux $ ./typelimits
arg = 250 --> non è maggiore di 200

Ok, l'errore di esecuzione è rimasto (e ci mancherebbe solo!), ma almeno sappiamo a cosa è dovuto e possiamo correggerlo facilmente. Questo è solo un esempio dei warning che può segnalare il -Wextra: spesso è veramente utile!

E, prima di chiudere, vorrei ricordarvi che tutti i flag semplici  hanno un flag parallelo che serve a negare (sopprimere) il warning in oggetto, ed è lo stesso flag però con un no- davanti. Quindi, se vogliamo sopprimere uno di quelli inclusi nel -Wall  (ad esempio il -Waddress) possiamo cambiare la linea di compilazione (che ho passato sopra) in questa maniera:

gcc -Wall -Wextra -pedantic -Wshadow -Wno-address

Questa linea modificata aggiungendo un -Wno-address ci permette di compilare segnalando tutti i warning utili ma senza mostrare quelli di tipo -Waddress; e a cosa serve questo? Beh, il compilatore GCC è un gran strumento, ma a volte può sbagliare anche lui offrendoci dei falsi negativi, oppure scambiando per errore cose che abbiamo fatto volutamente, oppure mostrandoci delle segnalazioni che riteniamo completamente ininfluenti: in questo caso possiamo "pulire" l'uscita della compilazione dai messaggi che non ci interessano.

E vabbè, penso che è giunto il momento di chiudere questa larga parentesi sui warning. Non so cosa ci riserverà il futuro... ma sarà sicuramente molto interessante! Restate sintonizzati!

Ciao, e al prossimo post!