Jaki jest rozmiar wyliczenia w języku C?


Najlepsza odpowiedź

Bardzo drażliwa zagadka, gdy trzeba dopasować przesunięcia struktury między granicami maszyny lub protokołami sieciowymi . Specyfikacja mówi, że rozmiar musi być minimalnym wymaganym do reprezentowania największej wartości, jaką może przyjąć. Na przykład typowe wyliczenie stanów automatu stanów może wymagać tylko bajtu.

AFAIK są tylko trzy sposoby kontrolowania rozmiaru:

Niektóre (ale nie wszystkie) kompilatory mają przełącznik kompilatora, aby wymusić rozmiar na sizeof (int) lub na 32 bity. Składnia przełącznika nie jest taka sama między powiedzmy MSC i gccc / clang.

Drugi sposób, jeśli chcesz wypełnić, powiedzmy, pole 16-bitowe, to zdefiniowanie fikcyjnej wartości maksymalnej, która wymaga tego rozmiaru.

Innym sposobem jest użycie regularnych stylów int w definicjach struktur, ale rezygnuje się z wygody debugera polegającego na tym, że wyświetla on wartość według nazwy wyliczenia i zamiast tego pobiera tylko liczby całkowite.

Wtedy prawdopodobnie będzie trzeba określić, że struktura zawierająca wyliczenie jest spakowana, a nie dopełniona.

Odpowiedź

Jeśli żadna część zmiennej x to const lub volatile, a x nie jest typu tablicowego, następnie instrukcja…

x = x;

… właściwie nic nie robi. Jest dobrze sformułowany. Zakładając, że x został wcześniej zainicjowany ¹ jest również dobrze zdefiniowany. Przypisanie nie Naprawdę zmieniaj stan programu, z kilkoma potencjalnymi wyjątkami (patrz poniżej).

Dzieje się tak, nawet jeśli x ma struct typ.

struct foo {

char buf[42];

int x;

};

struct foo x = { "Hello World", 18 };

x = x; // no big deal.

Więc na czym polega „błąd”? To nie jest przydatny kod, ale jest sporo kodu, który pasuje do tego opisu. To tylko błąd , jeśli zamierzałeś zrobić coś innego i przegapiłeś.

[Dodatek: Biorąc pod uwagę bezużyteczność tego zadania, kompilatory skłonne do ostrzeżenia o tym, ponieważ jest raczej prawdopodobne, że ma na myśli coś innego.]

Dlaczego trochę o const i volatile? Ograniczenie na const jest proste: oznacza „tylko do odczytu”, więc nie możesz w nim pisać. Przynajmniej nie z prostym przypisaniem.

Ten dotyczący volatile jest trudniejszy. To słowo kluczowe mówi, że kwalifikowana przez nie wartość może zostać zmodyfikowana w sposób niewidoczny dla implementacji, a zapis do niej może mieć skutki uboczne. Bez zagłębiania się tutaj w rolę, w praktyce jedyne rozsądnie dobrze zdefiniowane operacje, które można wykonać na ulotnym obiekcie, wyglądają następująco:

volatile int v;

int i;

i = v; // reads from v once, places into i.

v = i; // writes to v once with the contents of i.

Możesz zrobić trochę więcej rozsądnie z volatile, ale tylko nieznacznie. To temat na inny dzień. Jest powód, dla którego C ++ 20 wycofał wszystkie zastosowania volatile oprócz kilku z nich. Ich zmiany wyglądają całkiem rozsądnie.

Wspomniałem powyżej o kilku możliwych wyjątkach. Jonas Oberhauser przypomniał mi, że jakiekolwiek wypełnienie w struct jest niekoniecznie zachowane przez to przypisanie.

Ponadto teoretycznie jest możliwe, że podstawowa reprezentacja obiektów może się zmienić z powodu przypisania, jeśli ta sama wartość może być reprezentowana na wiele sposobów.

Mamy nadzieję, że Twój program jest niewrażliwy na obie możliwości. Jeśli jednak kiedykolwiek spojrzysz na podstawową reprezentację obiektu , możesz zauważyć różnice. Funkcje takie jak memcmp mogą na przykład zauważyć. Podobnie, jeśli stosujesz punningowanie typu .

¹ A jeśli nie, witamy w niezdefiniowanym zachowaniu, ponieważ prawdopodobnie napotkasz jedną z poniższych sytuacji:

  • Wartość obiektu z automatycznym czasem przechowywania jest używana, gdy jest nieokreślona ( 6.2 .4 , 6.7.9 , 6.8 ).
  • Wartość l oznaczająca obiekt o automatycznym okresie przechowywania, który mógł zostać zadeklarowany za pomocą klasy pamięci rejestru, jest używana w kontekście wymagającym wartość wyznaczonego obiektu, ale obiekt nie jest zainicjowany. ( 6.3.2.1 ).

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *