Jest stary post z prośbą o konstrukcję, dla której sizeof
wróci 0
. Istnieje kilka odpowiedzi z wysokimi wynikami od użytkowników o wysokiej reputacji, którzy twierdzą, że standardowo żaden typ ani zmienna nie może mieć rozmiaru 0. I zgadzam się z tym w 100%.
Jest jednak nowa odpowiedź, która przedstawia to rozwiązanie:
struct ZeroMemory {
int *a[0];
};
Właśnie miałem zagłosować i skomentować to, ale czas spędzony tutaj nauczył mnie sprawdzać nawet rzeczy, co do których jestem w 100% pewien. Więc ... ku mojemu zdziwieniu zarówno gcc
i clang
pokazują te same wyniki: sizeof(ZeroMemory) == 0
. Co więcej, rozmiar zmiennej to 0
:
ZeroMemory z{};
static_assert(sizeof(z) == 0); // Awkward...
Coaaa ...?
Jak to jest możliwe?
c++
language-lawyer
sizeof
bolov
źródło
źródło
Odpowiedzi:
Zanim C został ujednolicony, wiele kompilatorów nie miałoby trudności z obsługą typów o zerowym rozmiarze, o ile kod nigdy nie próbowałby odjąć jednego wskaźnika do typu o zerowym rozmiarze od drugiego. Takie typy były pożyteczne, a ich wspieranie było łatwiejsze i tańsze niż zakazanie im. Inni kompilatorzy zdecydowali się jednak zabronić takich typów, a niektóre kody z asercjami statycznymi mogły opierać się na fakcie, że wyskakiwałyby, gdyby kod próbował utworzyć tablicę o rozmiarze zerowym. Autorzy normy stanęli przed wyborem:
Zezwalaj kompilatorom na ciche akceptowanie deklaracji tablic o rozmiarze zerowym, nawet w przypadkach, gdy celem takich deklaracji byłoby wywołanie diagnostyki i przerwanie kompilacji, i wymaganie, aby wszystkie kompilatory akceptowały takie deklaracje (choć niekoniecznie po cichu) jako wytwarzające obiekty o rozmiarze zerowym .
Zezwalaj kompilatorom na ciche akceptowanie deklaracji tablic o rozmiarze zerowym, nawet w przypadkach, gdy celem takich deklaracji byłoby wyzwolenie diagnostyki i przerwanie kompilacji, oraz zezwolenie kompilatorom napotykającym takie deklaracje na przerwanie kompilacji lub kontynuowanie jej w wolnym czasie.
Wymagaj, aby implementacje wystawiały diagnostykę, jeśli kod deklaruje tablicę o rozmiarze zerowym, ale następnie zezwalaj implementacjom na przerwanie kompilacji lub kontynuowanie jej (z dowolną semantyką, którą uznają za odpowiednią) w wolnym czasie.
Autorzy Standardu zdecydowali się na # 3. W konsekwencji deklaracje tablic o rozmiarze zerowym są traktowane przez „rozszerzenie” standardu, mimo że takie konstrukcje były szeroko obsługiwane, zanim Standard ich zabronił.
Standard C ++ zezwala na istnienie pustych obiektów, ale w celu umożliwienia używania adresów pustych obiektów jako tokenów, nakazuje, aby miały one minimalny rozmiar 1. Dla obiektu, który nie ma elementów członkowskich, rozmiar 0 naruszałoby w ten sposób standard. Jeśli jednak obiekt zawiera elementy składowe o zerowej wielkości, C ++ Standard nie nakłada żadnych wymagań dotyczących sposobu przetwarzania poza faktem, że program zawierający taką deklarację musi wyzwolić diagnostykę. Ponieważ większość kodu używającego takich deklaracji oczekuje, że wynikowe obiekty będą miały rozmiar zerowy, najbardziej użytecznym zachowaniem dla kompilatorów odbierających taki kod jest traktowanie ich w ten sposób.
źródło
struct polygon { int count; POINT sides[0];}; struct { struct polygon poly; POINT pts[3]; } myTriangle = { {3}, {{1,1},{2,2},{2,1} };
. Należałoby zachować ostrożność, aby upewnić się, że wyrównanie nie powoduje problemów, ale można mieć obiekty o statycznym czasie trwania o różnych rozmiarach, które mogą być przetwarzane zamiennie.Jak wskazał Jarod42, tablice o zerowej wielkości nie są standardowymi C ++, ale rozszerzeniami GCC i Clang.
Dodanie
-pedantic
powoduje to ostrzeżenie:Zawsze zapominam, że
std=c++XX
(zamiaststd=gnu++XX
) nie wyłącza wszystkich rozszerzeń.To nadal nie wyjaśnia
sizeof
zachowania. Ale przynajmniej wiemy, że to nie jest standardowe ...źródło
sizeof(int*)
osobno (zakładam): coliru.stacked-crooked.com/a/e9a3038bf587b65csizeof
daje dla tego typu, również nie mają zastosowania. Zachowanie jest zatem decyzją podjętą przez programistów kompilatora.ZeroMemory* z = (ZeroMemory*)malloc(sizeof(ZeroMemory) + 2 * sizeof(int*)); z.a[1] = new int{1}; /* or whatever, just use indices into a to access dynamic memory after the previous members (in this case: none) */
taki, że oczywiście nie jest to zgodne ze standardem! (Może to być użyte do łatwego analizowania dynamicznej długości komunikatów sieciowych, na przykład przez rzutowanie do struktury).W C ++ tablica o rozmiarze zerowym jest niedozwolona.
ISO / IEC 14882: 2003 8.3.4 / 1:
g ++ wymaga, aby
-pedantic
flaga ostrzegała o tablicy o rozmiarze zerowym.źródło
Tablice o zerowej długości są rozszerzeniem GCC i Clang. Zastosowanie
sizeof
do tablic o zerowej długości daje zero .Klasa C ++ (pusta) nie może mieć rozmiaru
0
, ale pamiętaj, że klasaZeroMemory
nie jest pusta. Ma nazwany element członkowski o rozmiarze0
i zastosowaniesizeof
zwróci zero.źródło
0
, ale niepusta klasa może mieć rozmiar0
... to nie ma większego sensu. Ale wydaje mi się, że jest to rodzaj konfliktu, jaki można spotkać w przypadku niestandardowych rozszerzeń.0
. W tym konkretnym przypadku element struktury to self ma rozmiar 0, co powoduje, że rozmiar struktury 0. Wiem, że jest to zagmatwane, ale starałem się jak najlepiej wyjaśnić.