programowanieEmbedded/Low-level C developer

Wyjaśnij, jak działają unie (union) w języku C. Jakie zadania są dzięki nim rozwiązywane, jak ich poprawnie używać i jakie typowe pułapki można napotkać przy ich użyciu?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Union (unie) to specjalny typ danych, który przechowuje różne wartości w tej samej przestrzeni pamięci. W union wszystkie człony znajdują się w pamięci pod tym samym adresem, a wielkość pamięci odpowiada rozmiarowi największego członu.

Zastosowanie:

  • wygodne do oszczędzania pamięci, jeśli zmienna w danym momencie przyjmuje jeden z kilku typów wartości;
  • pozwala na interpretację tych samych bitów na różne sposoby (na przykład dla niskopoziomowego dostępu lub protokołów).

Przykład:

union Data { int i; float f; char s[4]; }; union Data d; d.i = 0x41424344; // w pamięci teraz 4 bajty, które można odczytać jako int, float, string printf("%c%c%c ", d.s[0], d.s[1], d.s[2]); // specyficzny dla dostawcy output

Pułapki i zasady użytkowania:

  • Trzymaj w union w danym momencie tylko jedno „istotne” pole.
  • Nie ma automatycznego śledzenia, które pole było „aktywne” ostatnio.
  • Odczyt nieaktywnego pola — niedozwolone zachowanie.
  • Bardzo istotna jest kolejność bajtów na platformie (endianness!)

Pytanie z pułapką

Pytanie: Jaki jest sens używania union, skoro można po prostu użyć struktury z kilkoma polami?

Odpowiedź: Użycie union oszczędza pamięć, ponieważ w danym momencie przechowuje TYLKO JEDNO wartości, a nie wszystkie naraz. W strukturze dla każdego pola przydzielana jest pamięć, a w union — tylko dla największego z pól, pozostałe „dzielą” tę pamięć. Ponadto union pozwala na bezpieczną lub zamierzoną konwersję między różnymi reprezentacjami danych w jednym fragmencie pamięci.

Przykład:

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))

Przykłady rzeczywistych błędów


Historia

W projekcie sterownika urządzenia komunikacja z „hardwarem” odbywała się przez union z bitowym dostępem do danych. Po niewielkiej refaktoryzacji programista zaczął pisać do niewłaściwego pola union, co doprowadziło do odczytu nieaktualnych danych i katastrofalnych awarii systemu, ponieważ w union tylko jedno pole było „rzeczywiste” w danym momencie.


Historia

Przy wymianie pakietów sieciowych przez union do zarządzania pamięcią programista zapomniał uwzględnić wyrównanie struktur. W rezultacie wystąpiło przesunięcie o jeden bajt, a struktura była analizowana z niepoprawnymi przesunięciami, co sprawiło, że protokół stał się niekompatybilny z oryginałem.


Historia

W pracy nad biblioteką serializacji programista przekazywał union przez wartość do funkcji, gdzie wymagane pole nie było inicjowane przed odczytem. W związku z tym dane były niepoprawnie serializowane, w wyjściowym strumieniu pojawiał się szum, a przywrócenie początkowych informacji było niemożliwe.