ProgrammazioneSviluppatore C Embedded/Low-level

Spiega come funzionano le unioni (union) nel linguaggio C. Quali compiti vengono risolti con il loro aiuto, come utilizzarli correttamente e quali tipiche insidie si incontrano nel loro uso?

Supera i colloqui con l'assistente IA Hintsage

Risposta

Union (unione) è un tipo di dato speciale che memorizza valori diversi nella stessa area di memoria. In un union tutti i membri occupano la stessa posizione in memoria e la dimensione della memoria è pari alla dimensione del membro più grande.

Utilizzo:

  • utile per risparmiare memoria se una variabile può assumere uno tra diversi tipi di valori in un dato momento;
  • consente di interpretare gli stessi bit in modi diversi (ad esempio, per accesso a basso livello o protocolli).

Esempio:

union Data { int i; float f; char s[4]; }; union Data d; d.i = 0x41424344; // ora in memoria ci sono 4 byte che possono essere letti come int, float, stringa printf("%c%c%c\n", d.s[0], d.s[1], d.s[2]); // output specifico del vendor

Insidie e regole d'uso:

  • Mantieni nell'union solo un campo "significativo" alla volta.
  • Non c'è tracciamento automatico di quale campo è stato "attivo" per ultimo.
  • La lettura di un campo inattivo – undefined behavior.
  • È molto importante l'ordine dei byte sulla piattaforma (endian!)

Domanda trabocchetto

Domanda: Qual è il senso di usare union quando si può semplicemente usare una struttura con più campi?

Risposta: Usare union risparmia memoria, perché in qualsiasi momento contiene solo UN valore, non tutti contemporaneamente. In una struttura, per ogni campo viene riservata memoria, mentre in un union solo per il campo massimo, gli altri "condividono" questa memoria. Inoltre, l'union consente di eseguire una conversione sicura o intenzionale tra diverse rappresentazioni di dati su un frammento di memoria.

Esempio:

struct S { int i; float f; } s; // sizeof = sizeof(int) + sizeof(float) union U { int i; float f; } u; // sizeof = max(sizeof(int),sizeof(float))

Esempi di errori reali


Storia

In un progetto di driver di dispositivo, la comunicazione con l'hardware avveniva tramite union con accesso bit ai dati. Dopo una piccola rifattorizzazione, lo sviluppatore ha iniziato a scrivere nel campo sbagliato dell'union, il che ha portato alla lettura di dati non validi e a gravi guasti di sistema, poiché nell'union solo un campo è "reale" in ogni momento.


Storia

Durante lo scambio di pacchetti di rete tramite union per la gestione della memoria, lo sviluppatore ha dimenticato di considerare l'allineamento delle strutture. Di conseguenza, si è verificato uno spostamento di un byte e la struttura è stata analizzata con spostamenti errati, rendendo il protocollo incompatibile con l'originale.


Storia

Lavorando su una libreria di serializzazione, il programmatore ha passato un union per valore a una funzione, dove il campo necessario non è stato inizializzato prima della lettura. Questo ha causato una serializzazione errata dei dati, con spazzatura nel flusso di output, e ripristinare le informazioni originali è stato impossibile.