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.

domenica 13 ottobre 2013

La union fa la forza
come usare le union in C

Alzi la mano chi ha scritto recentemente codice che usa le union. Oppure, alzi la mano, senza prima andare a rileggersi un manuale del C, chi di voi sa usare e/o descrivere perfettamente le union ? Uhmm... vedo poche mani alzate. Il fatto è che le union sono una di quelle parti del C un po' misconosciute, di uso dubbio e infrequente, insomma una di quelle parti di cui, per mancanza di pratica, ci si scorda.

Per introdurvi l'argomento (e, ripeto, leggete senza consultare prima un manuale del C, se no non potete mettervi alla prova), vi racconterò una storia vera. Un tipo che conosco, le cui iniziali sono A.A. (di più non posso dirvi per questioni di privacy) andò, molto tempo fa, a un colloquio di lavoro, e gli proposero un semplice test come questo (più o meno):
#include <stdio.h>

void main(void)
{
    union u_test {
        int  i;
        char s[4];
    } my_test;

    my_test.i = 0x01020304;

    // cosa stampa la seguente linea?
    printf("s[0]=%d s[1]=%d s[2]=%d s[3]=%d\n",
        my_test.s[0], my_test.s[1], my_test.s[2], my_test.s[3]);
}
Ecco, per i motivi indicati all'inizio del post (uso dubbio e infrequente), il nostro amico che non usava una union da almeno 15 anni (si, 15 anni!), rimase inizialmente perplesso, sforzò la memoria e non cadde nel 1º livello di errore che induce questo test un po' ingannevole, 1º livello di errore che ti indurrebbe a dire:

ho inizializzato il campo i e vado a leggere il campo s: ma allora la printf() stampa valori indefiniti!

No, lui si ricordava che "le union allocano memoria per la più grande delle variabili interne, variabili che condividono lo stesso spazio di memoria", quindi non cadde nel tranello, e rispose quasi perfettamente... quasi, perché cadde nel 2º livello di errore, quello che ti porta a dire:

ho inizializzato i, però leggo s, che è come se fosse stata inizializzata anche lei, quindi la riposta è: s[0]=1 s[1]=2 s[2]=3 s[3]=4 !

Ok, quasi giusto: in realtà la risposta è:

s[0]=4 s[1]=3 s[2]=2 s[3]=1

Il nostro amico A.A. se ne rese conto tornando a casa, quando l'errore ormai era fatto: nella locazione s[0] ci finisce la parte meno significativa di i (quindi 0x04), e così via gli altri numeri, per cui il risultato era in ordine inverso. Va beh, cose che succedono...

Se cercate in rete con google troverete tanti utili esempi d'uso delle union, tra i quali, direi, i più interessanti sono quelli per il trattamento di dati con contenuto variabile (protocolli di comunicazione, per esempio) in cui si vuole trattare, con la stessa struttura dati, informazioni di tipo diverso, e interpretando la union, di volta in volta, nella maniera corretta selezionando un opportuno flag mantenuto dal programma. Questo permette di fare programmi flessibili e risparmiosi di memoria (rispetto all'uso delle struct), cosa che in alcuni ambienti di sviluppo potrebbe risultare utile.

Occhio però: per la maniera stessa in cui funzionano le union sono flessibili ma ingannevoli, e gli errori subdoli di funzionamento sono dietro l'angolo che aspettano. Bisogna essere molto rigorosi nel mantenere, e testare, il flag di stato che ci indica quale personalità della union stiamo usando in ogni momento.

Ciao e al prossimo post.

Questo post è ispirato ad una storia vera, ma ogni riferimento a fatti o persone realmente esistiti è puramente casuale (uhmm ???)