Dlaczego spakowane struktury nie są częścią języka C?

10

Każdy kompilator C oferuje opcję „spakowania” struktur C (np. __attribute__ ((__packed__))Lub #pragma pack()). Teraz wszyscy wiemy, że pakowanie jest wymagane, jeśli chcemy wysyłać lub przechowywać dane w niezawodny sposób. Musiało to być również wymagane od pierwszych dni języka C.

Zastanawiam się więc, dlaczego spakowane struktury nie są częścią specyfikacji języka C? Nie ma ich nawet w C99 ani C11, chociaż konieczność ich posiadania jest znana od dziesięcioleci? Czego mi brakuje? Dlaczego jest specyficzny dla kompilatora?

grasbueschel
źródło
2
Nie konieczne do pisania czystego kodu C.
user253751,

Odpowiedzi:

7

Chyba dlatego, że zależy to od kombinacji docelowego procesora / kompilatora. Oznacza to, że lepiej być dyrektywą kompilatora (ponieważ jest z tym związana) niż aspekt językowy, ponieważ jak to określić? Jedyny sposób, w jaki mogliby to zrobić, to zjednoczenie.

Artykuł Raymonda daje pewien wgląd w to, dlaczego: http://www.catb.org/esr/structure-packing/

Frans Bouma
źródło
Bardzo ciekawy artykuł. (+1)
Giorgio
Jaka byłaby trudność, gdyby kod powiedział: „Potrzebuję struktury zawierającej 12 bajtów; pole X musi zachowywać się jak 32-bitowa liczba całkowita przechowywana jako cztery oktety little-endian przy przesunięciu 0; pole Y musi zachowywać się jak 64-bitowa liczba całkowita przechowywane jako bajty oktetów little-endian z przesunięciem 4 "? Kod do obsługi tego na dowolnej platformie nie powinien być gorszy od tego, co kompilatory muszą już mieć dla pól bitowych, a w przypadkach, gdy programista określa wyrównanie pasujące do maszyny natywnej, może być znacznie wydajniejsze. Na innych komputerach byłby mniej wydajny, ale nadal przenośny.
supercat
5

Istnieją trzy główne czynniki.

  1. Niektóre procesory nie mogą uzyskać dostępu do niezaangażowanych danych (na przykład liczba całkowita lub liczba zmiennoprzecinkowa rozpoczynająca się od nieparzystego adresu). Próba wykonania powoduje wyjątek.
  2. Niektóre procesory mogą uzyskiwać dostęp do niezaangażowanych danych, ale kosztem wydajności.
  3. Dostęp do większości struktur ma jeden zestaw kodu źródłowego C / C ++, a interoperacyjność z innymi językami jest wyjątkiem, a nie regułą.

Biorąc pod uwagę te czynniki, zarówno standardowy, jak i wszystkie kompilatory C / C ++ rutynowo wypełniają struktury, aby zapewnić optymalne wyrównanie procesora, ale także zapewniają mechanizmy do obejścia tego, jeśli jest to konieczne do celów interop.

To w żadnym wypadku nie zostało przeoczone. Jest to bardzo dobrze zrozumiane, a obecna sytuacja jest z założenia. Najnowsze wersje standardu C ++ mają szerokie wsparcie dla obsługi problemów z wyrównaniem, których być może nie znasz.

david.pfx
źródło
Dowolny argument, który można by wysunąć przeciwko spakowanym strukturom, można również wykorzystać do uzasadnienia uczynienia pól bitowych opcją opcjonalną. Dostęp do elementów spakowanych struktur byłby powolny na niektórych procesorach, szybki na innych, ale kompilatory próbują zastąpić obejścia kodu użytkownika z powodu braku funkcji nieprzyrównanego dostępu bardziej wydajnym kodem, jest o wiele bardziej skomplikowane niż zwykłe kompilatory pozwalające programistom określić, co oni potrzebują.
supercat
@supercat: o co walczysz (lub przeciw)? Nie rozumiem
david.pfx
Uważam, że pola bitowe powinny być opcjonalne, ale jeśli pola bitowe będą obowiązkową funkcją, warto je rozszerzyć w sposób umożliwiający wyraźną kontrolę nad układem struktury. W przeciwnym razie efekt netto jest taki, że kompilatory muszą wykonać 90% pracy, która byłaby potrzebna do pełnej kontroli układu, ale programiści czerpią tylko 10% korzyści.
supercat
@ superupat: pola bitowe są liczbami całkowitymi i są zgodne z tymi samymi zasadami porządkowania układu bitów, co liczby całkowite: zdefiniowano implementację. Elementy konstrukcji są uporządkowane zgodnie z zadeklarowanymi granicami znaków, prawdopodobnie z włożonym opakowaniem. Są koncepcyjnie całkiem odrębne. [Będziesz musiał zadać kolejne pytanie, jeśli chcesz rozwinąć swoją propozycję, ale nie sądzę, żeby to w ogóle zadziałało.]
david.pfx
0

Jest specyficzny dla kompilatora, ponieważ nie znajduje się w standardzie. I nie jest to w standardzie, ponieważ byłoby trudne do określenia w sposób, który nie wymagałby dużo wysiłku implementacyjnego dla kompilatorów niejasnych platform z wymuszonymi ograniczeniami wyrównania.

I żaden z tych wysiłków nie ma większego uzasadnienia, ponieważ każdy kompilator / platforma, o którą dba każdy korzystający z kompilatora C89 lub nowszego, już go ma.

soru
źródło
2
??? Odpowiedziałeś na pytanie „Dlaczego nie jest w standardowym języku”, mówiąc „ponieważ nie ma w standardowym języku” ...
Emilio Garavaglia
Tak myślałem najpierw, ale z drugiej strony można określić funkcję typu „jeśli struct jest zdefiniowane za pomocą słowa kluczowego„ spakowany ”, jego rozmiar będzie gwarantowany na tym samym poziomie, co dodany rozmiar każdego elementu członkowskiego. Na platformach, które nie obsługują nieprzyrównany dostęp do pamięci, dostęp do jednej z wartości elementów struct jest niezdefiniowanym zachowaniem. ” Umożliwiłoby to programistom na platformach bez nieograniczonego dostępu przynajmniej znajomość wielkości struktur i przesunięcia poszczególnych elementów ...
grasbueschel
1
Byłoby możliwe, aby dostęp nieprzystosowany działał w systemach, które nie obsługują go sprzętowo, poprzez implementację takich struktur jak tablica bajtów i wykonanie niezbędnych operacji przesunięcia bitów i &/ / |operacji odczytu / zapisu wartości każdego pola.
dan04,
1
@ dan04: Na wielu procesorach kompilator może generować kod dla nieprzyrównanego dostępu, który był bardziej wydajny niż użycie sekwencji odczytów i przesunięć bajtów. Posiadanie takiej składni ułatwiłoby takim kompilatorom generowanie wydajnego kodu, niż wymaganie od nich rozpoznania wszystkich innych sposobów, w jakie programiści mogą próbować pisać kod w celu złożenia bajtów w dłuższe typy.
supercat