Dlaczego sizeof (my_arr) [0] kompiluje się i ma taką samą wielkość sizeof (my_arr [0])?

129

Dlaczego ten kod się kompiluje?

_Static uint32_t my_arr[2];
_Static_assert(sizeof(my_arr) == 8, "");
_Static_assert(sizeof(my_arr[0]) == 4, "");
_Static_assert(sizeof(my_arr)[0] == 4, "");

Pierwsze 2 potwierdzenia są oczywiście poprawne, ale spodziewałbym się, że ostatni wiersz zakończy się niepowodzeniem, ponieważ rozumiem, że sizeof()powinien być przeliczany na literał całkowity, którego nie można traktować jako tablicy. Innymi słowy, zakończy się niepowodzeniem w taki sam sposób, jak następująca linia:

_Static_assert(4[0] == 4, "");

Co ciekawe, poniższe rzeczy rzeczywiście nie dają się skompilować (co powinno robić to samo, nie?):

_Static_assert(*sizeof(my_arr) == 4, "");

błąd: niepoprawny argument typu jednoargumentowego „*” (ma „long unsigned int”) _Static_assert (* sizeof (my_arr) == 4, „”);

Jeśli to ma znaczenie, używam gcc 5.3.0

bgomberg
źródło
15
Podejrzewam, że ( sizeof( my_arr ) )[ 0 ]zawodzi.
Andrew Henle
Niedawny duplikat ma inną odmianę tej składni, która jest niespodzianką: Dlaczego kompiluje się sizeof (x) ++?
Peter Cordes

Odpowiedzi:

197

sizeofnie jest funkcją. Jest to operator jednoargumentowy, taki jak !lub ~.

sizeof(my_arr)[0]parses as sizeof (my_arr)[0], co jest tylko sizeof my_arr[0]z nadmiarowymi nawiasami.

To jest tak samo jak !(my_arr)[0]parsowanie jako !(my_arr[0]).

Ogólnie rzecz biorąc, operatory przyrostkowe mają wyższy priorytet niż operatory przedrostkowe w C. sizeof *a[i]++parses as sizeof (*((a[i])++))(operatory przyrostkowe []i ++są stosowane anajpierw do operatorów przedrostkowych *i sizeof).

(To jest wersja wyrażenia sizeof. Istnieje również wersja typu, która przyjmuje nazwę typu w nawiasach sizeof (TYPE):. W takim przypadku parametry byłyby wymagane i byłyby częścią sizeofskładni).

melpomene
źródło
14
Zdecydowanie wiedziałem, że sizeof jest operatorem jednoargumentowym, a nie funkcją, ale całkowicie zapomniałem. Ups. Dzięki za szczegółowe wyjaśnienie. Fakt, że [] ma wyższy priorytet niż *, jest interesujący niezależnie od tego.
bgomberg
@melpomene Interesting. Nigdy nie myślałem o sizeofbyciu jednoargumentowym operatorem.
mutantkeyboard
5
Nie masz na myśli "... parses as sizeof (my_arr[0])"? Samo dodanie spacji tak naprawdę niczego nie zmienia.
Bernhard Barker
Polecam sizeof((my_array)[0])zamiast tego
bolov
46

sizeofma dwie „wersje”: sizeof(type name)i sizeof expression. Ten pierwszy wymaga pary ()wokół argumentu. Ale ten drugi - ten z wyrażeniem jako argumentem - nie ma ()wokół swojego argumentu. Cokolwiek ()użyjesz w argumencie, jest postrzegane jako część wyrażenia argumentu, a nie część sizeofsamej składni.

Ponieważ my_arrjest znany kompilatorowi jako nazwa obiektu, a nie nazwa typu, sizeof(my_arr)[0]kompilator w rzeczywistości widzi twoją jako sizeofzastosowaną do wyrażenia:, sizeof (my_arr)[0]gdzie (my_arr)[0]jest wyrażeniem argumentu. ()Otaczający nazwa tablicy jest czysto zbędne. Całe wyrażenie jest interpretowane jako sizeof my_arr[0]. Jest to odpowiednik twojego poprzedniego sizeof(my_arr[0]).

(Oznacza to, przy okazji, że twój poprzedni sizeof(my_arr[0])zawiera również parę zbędnych ().)

Jest to dość rozpowszechnione nieporozumienie, że sizeofskładnia w jakiś sposób wymaga par ()argumentów. To błędne przekonanie wprowadza w błąd intuicję ludzi podczas interpretowania takich wyrażeń, jak sizeof(my_arr)[0].

Mrówka
źródło
1
Pierwsza wersja istnieje tak, że można ogólnie sprawdzić rozmiar liczby całkowitej na maszynie (od czasów, gdy nie było nawet maszyn 64-bitowych!), Ale intnie jest prawidłowym wyrażeniem, więc nie można użyć druga forma z nim.
NH.
26

[]mają wyższą prekendencję niż sizeof. Tak sizeof(my_arr)[0]samo jak sizeof((my_arr)[0]).

Oto link do tabeli pierwszeństwa.

mch
źródło
8

Używasz wersji sizeofoperatora, która przyjmuje wyrażenie jako parametr. W przeciwieństwie do tego, który przyjmuje typ, nie wymaga nawiasów. Stąd operand jest prosty (my_arr)[0], a nawiasy są zbędne.

Quentin
źródło