Ce face union
Un union se declară ca un struct, însă câmpurile nu stau unul după altul, ci se suprapun. Dimensiunea lui este cea a câmpului cel mai mare (plus eventual aliniere). Când scrii un câmp, îl suprascrii pe cel scris înainte.
Se folosește pentru a economisi memorie când o valoare poate fi de mai multe tipuri, dar niciodată de mai multe deodată. De obicei îl pui într-un struct, împreună cu un câmp (de multe ori un enum) care spune ce tip este activ acum: o „uniune etichetată”.
În C, citirea altui câmp decât cel scris ultima dată reinterpretează octeții (type punning); standardul permite asta, dar rezultatul depinde de reprezentarea tipurilor pe acel calculator.
Exemple
Exemplul 1: Câmpurile împart memoria
sizeof uniunii este cel al câmpului mare (double, 8 octeți), nu suma lor, ca la struct.
#include <stdio.h>
union valoare {
int i;
double d;
char c;
};
struct toate {
int i;
double d;
char c;
};
int main(void) {
printf("sizeof(union valoare) = %zu\n", sizeof(union valoare));
printf("sizeof(struct toate) = %zu\n", sizeof(struct toate));
union valoare v;
v.i = 42;
printf("v.i = %d\n", v.i);
v.d = 3.75; // suprascrie v.i
printf("v.d = %.2f\n", v.d);
return 0;
}
Rezultat
sizeof(union valoare) = 8
sizeof(struct toate) = 24
v.i = 42
v.d = 3.75
Exemplul 2: Uniune etichetată: o valoare de mai multe tipuri
Câmpul tip spune care membru al uniunii este valid. Funcția de afișare citește doar membrul corect.
#include <stdio.h>
enum tip_camp { NUMAR, PRET, TEXT };
struct camp {
enum tip_camp tip;
union {
int numar;
double pret;
const char *text;
}; // uniune anonimă (C11): câmpurile se accesează direct
};
void afiseaza(struct camp c) {
switch (c.tip) {
case NUMAR: printf("număr: %d\n", c.numar); break;
case PRET: printf("preț: %.2f lei\n", c.pret); break;
case TEXT: printf("text: %s\n", c.text); break;
}
}
int main(void) {
struct camp campuri[] = {
{.tip = TEXT, .text = "Ana"},
{.tip = NUMAR, .numar = 7},
{.tip = PRET, .pret = 19.9},
};
for (size_t i = 0; i < 3; i++) afiseaza(campuri[i]);
return 0;
}
Rezultat
text: Ana
număr: 7
preț: 19.90 lei
Exemplul 3: Aceiași octeți văzuți în două feluri
Uniunea arată octeții unui uint32_t. Ordinea depinde de procesor: pe little-endian primul octet este cel mai puțin semnificativ (0x44).
#include <stdio.h>
#include <stdint.h>
union cuvant {
uint32_t numar;
uint8_t octeti[4];
};
int main(void) {
union cuvant u = {.numar = 0x11223344};
for (int i = 0; i < 4; i++) printf("octeti[%d] = 0x%02x\n", i, u.octeti[i]);
return 0;
}
Rezultat
octeti[0] = 0x44
octeti[1] = 0x33
octeti[2] = 0x22
octeti[3] = 0x11
Greșeli frecvente
- Crezi că un
unionpăstrează toate câmpurile, ca unstruct. Doar ultimul scris este valid; ține minte care, de obicei cu un câmpenumseparat. - Folosești uniuni pentru a converti între tipuri când ai nevoie de o conversie de valoare.
(int)3.7dă 3; citirea octeților unuidoublecaintprintr-o uniune dă cu totul altceva. - Inițializezi o uniune cu mai multe valori:
union valoare v = {1, 2.5};. Se inițializează doar primul câmp; pentru altul folosește{.d = 2.5}.
Comenzi înrudite
struct: Grupează mai multe valori de tipuri diferite într-un singur tip nou, de exemplu numele, vârsta și orașul unei persoane.enum: Definește un set de constante întregi cu nume, de exemplu zilele săptămânii sau stările unei comenzi.sizeof: Operatorul care spune câți octeți ocupă un tip sau o variabilă; calculat de compilator, rezultatul are tipul size_t.switch: Alege una din mai multe ramuri după valoarea unui întreg, cu etichete case; fără break, execuția continuă în ramura următoare.stdint.h și limits.h: Tipuri întregi cu dimensiune exactă (int32_t, uint8_t) și constantele cu valorile minime și maxime ale tipurilor (INT_MAX).