Ciekawe, co się właściwie stanie, jeśli zdefiniuję int array[0];
w kodzie tablicę o zerowej długości ? GCC w ogóle nie narzeka.
Przykładowy program
#include <stdio.h>
int main() {
int arr[0];
return 0;
}
Wyjaśnienie
Właściwie próbuję dowiedzieć się, czy tablice o zerowej długości zainicjowane w ten sposób, zamiast wskazywać na zmienną długość w komentarzach Darhazera, są zoptymalizowane, czy nie.
Dzieje się tak, ponieważ muszę wypuścić jakiś kod na wolność, więc próbuję dowiedzieć się, czy muszę obsługiwać przypadki, w których SIZE
zdefiniowano jako 0
, co zdarza się w niektórych kodzie ze zdefiniowanym statycznieint array[SIZE];
Zaskoczyło mnie, że GCC nie narzeka, co doprowadziło do mojego pytania. Z otrzymanych odpowiedzi wynika, że brak ostrzeżenia jest w dużej mierze spowodowany obsługą starego kodu, który nie został zaktualizowany nową składnią [].
Ponieważ zastanawiałem się głównie nad błędem, oznaczam odpowiedź Lundina jako poprawną (odpowiedź Nawaza była pierwsza, ale nie była tak kompletna) - inni wskazywali jej faktyczne zastosowanie w konstrukcjach wyściełanych ogonem, chociaż istotne, nie jest dokładnie to, czego szukałem.
[]
w Pythonie, a nawet""
w C? Czasami masz funkcję lub makro, które wymaga tablicy, ale nie masz żadnych danych do umieszczenia w niej.Odpowiedzi:
Tablica nie może mieć zerowego rozmiaru.
ISO 9899: 2011 6.7.6.2:
Powyższy tekst jest prawdziwy zarówno dla zwykłej tablicy (akapit 1). W przypadku VLA (tablicy o zmiennej długości) zachowanie jest niezdefiniowane, jeśli wartość wyrażenia jest mniejsza lub równa zero (akapit 5). To jest tekst normatywny w standardzie C. Kompilator nie może zaimplementować go inaczej.
gcc -std=c99 -pedantic
daje ostrzeżenie dla przypadku innego niż VLA.źródło
#error
dyrektywy.#error
dyrektywy preprocesora.Zgodnie ze standardem nie jest to dozwolone.
Jednak obecną praktyką w kompilatorach C jest traktowanie tych deklaracji jako deklaracji elastycznego elementu tablicy ( FAM ) :
Standardowa składnia FAM to:
struct Array { size_t size; int content[]; };
Chodzi o to, że następnie przydzielisz go tak:
void foo(size_t x) { Array* array = malloc(sizeof(size_t) + x * sizeof(int)); array->size = x; for (size_t i = 0; i != x; ++i) { array->content[i] = 0; } }
Możesz też użyć go statycznie (rozszerzenie gcc):
Array a = { 3, { 1, 2, 3 } };
Jest to również znane jako struktury wyściełane ogonem (termin ten poprzedza publikację C99 Standard) lub struct hack (dzięki Joe Wreschnigowi za wskazanie tego).
Jednak ta składnia została ustandaryzowana (a efekty gwarantowane) dopiero niedawno w C99. Wcześniej stały rozmiar był konieczny.
1
był przenośny, choć był raczej dziwny.0
był lepszy w wskazywaniu zamiaru, ale nie był zgodny z prawem, jeśli chodzi o Standard i był obsługiwany jako rozszerzenie przez niektóre kompilatory (w tym gcc).Jednak praktyka wyściełania ogonem opiera się na fakcie, że miejsce do przechowywania jest dostępne (ostrożnie
malloc
), więc ogólnie nie nadaje się do używania w stosie.źródło
int content[];
tutaj, o ile rozumiem. Ponieważ nie jestem zbyt obeznany z językiem C tej sztuki ... czy mógłbyś potwierdzić, czy moje rozumowanie wydaje się poprawne?foo[x]
abyfoo[0]
zawszefoo
było tablicą jednoelementową.W standardowym C i C ++ tablice o rozmiarze zerowym nie są dozwolone.
Jeśli używasz GCC, skompiluj go z
-pedantic
opcją. Daje ostrzeżenie , mówiąc:W przypadku C ++ daje podobne ostrzeżenie.
źródło
error C2466: cannot allocate an array of constant size 0
[BCC32 Error] test.c(3): E2021 Array must have at least one element
-pedantic -Werror
możesz też po prostu zrobić-pedantic-errors
std::array
. (Na marginesie: Pamiętam, ale nie może znaleźć źródło, że Włas zostały rozważone i wyraźnie odrzucił od bycia w C ++).Jest to całkowicie nielegalne i zawsze było, ale wielu kompilatorów zaniedbuje sygnalizowanie błędu. Nie wiem, dlaczego chcesz to zrobić. Jedynym zastosowaniem, o którym wiem, jest wywołanie błędu czasu kompilacji z wartości logicznej:
char someCondition[ condition ];
Jeśli
condition
jest fałszem, pojawia się błąd czasu kompilacji. Ponieważ kompilatory na to pozwalają, postanowiłem użyć:char someCondition[ 2 * condition - 1 ];
Daje to rozmiar 1 lub -1, a nigdy nie znalazłem kompilatora, który zaakceptowałby rozmiar -1.
źródło
STATIC_ASSERT
go wykorzystały.#if condition \n #error whatever \n #endif
Dodam, że na ten argument znajduje się cała strona dokumentacji online gcc.
Niektóre cytaty:
i
więc możesz
int arr[0] = { 1 };
i bum :-)
źródło
int a[0]
, toa[0] = 1
a[1] = 2
??a[100000] = 5
ale jeśli masz szczęście, po prostu zawiesisz swoją aplikację, jeśli masz szczęście: -)Innym zastosowaniem tablic o zerowej długości jest tworzenie obiektów o zmiennej długości (przed C99). Macierze zerowej długości są różne od elastycznych tablic które [] Nie 0.
Cytat z dokumentu gcc :
Rzeczywisty przykład to tablice o zerowej długości
struct kdbus_item
w kdbus.h (moduł jądra Linuksa).źródło
void*
arytmetyczne (więc dodawanie lub odejmowanie wskaźników do obiektów o zerowej wielkości byłoby zabronione). Podczas gdy elastyczne składowe tablicy są w większości lepsze niż tablice o zerowej wielkości, mogą one również działać jako rodzaj „unii” aliasów bez dodawania dodatkowego poziomu „syntaktycznego” pośrednictwa do tego, co następuje (np. Mającstruct foo {unsigned char as_bytes[0]; int x,y; float z;}
dostęp do członkówx
.z
...myStruct.asFoo.x
itd. Ponadto IIRC, C wrzeszczy przy każdej próbie włączenia elastycznego elementu tablicy do struktury, uniemożliwiając w ten sposób uzyskanie struktury, która zawiera wiele innych elementów elastycznej tablicy o znanej długości zadowolony.sizeof x->contents
jest błędem w ISO C w przeciwieństwie do zwracania 0 w gcc. Tablice o zerowej wielkości, które nie są elementami strukturalnymi, powodują szereg innych problemów.struct {int n; THING dat[];}
i może pracować z elementami o statycznym lub automatycznym czasie trwania.Deklaracje tablic o zerowym rozmiarze w strukturach byłyby przydatne, gdyby były dozwolone, a semantyka byłaby taka, że (1) wymuszałyby wyrównanie, ale w przeciwnym razie nie przydzielałyby żadnej spacji, oraz (2) indeksowanie tablicy byłoby uważane za zdefiniowane zachowanie w przypadek, w którym wynikowy wskaźnik znajdowałby się w tym samym bloku pamięci co struktura. Takie zachowanie nigdy nie było dozwolone przez żaden standard C, ale niektóre starsze kompilatory zezwalały na to, zanim stało się standardem dla kompilatorów, zezwalając na niekompletne deklaracje tablic z pustymi nawiasami.
Struct hack, powszechnie implementowany przy użyciu tablicy o rozmiarze 1, jest podejrzany i nie sądzę, aby istniał żaden wymóg, aby kompilatory go powstrzymywały. Na przykład spodziewałbym się, że jeśli kompilator zobaczy
int a[1]
, będzie miał prawo traktować goa[i]
jako plika[0]
. Jeśli ktoś próbuje obejść problemy z wyrównaniem struktury, włamać się za pomocą czegoś takiego jakkompilator może sprytnie i założyć, że rozmiar tablicy naprawdę wynosi cztery:
Taka optymalizacja może być rozsądna, zwłaszcza jeśli
myStruct->data
mogłaby zostać załadowana do rejestru w tej samej operacji, comyStruct->size
. W standardzie nie znam niczego, co zabraniałoby takiej optymalizacji, chociaż oczywiście złamałby każdy kod, który mógłby oczekiwać dostępu do rzeczy poza czwartym elementem.źródło