Perché sizeof (my_arr) [0] viene compilato e uguale sizeof (my_arr [0])?


129

Perché viene compilato questo codice?

_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, "");

I primi 2 assertori sono ovviamente corretti, ma mi sarei aspettato che l'ultima riga fallisse, poiché la mia comprensione è che sizeof()dovrebbe valutare un valore intero letterale, che non può essere trattato come un array. In altre parole, fallirebbe nello stesso modo in cui fallisce la seguente riga:

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

È interessante notare che il seguente non riesce a compilare (che dovrebbe fare la stessa cosa, no?):

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

errore: argomento di tipo non valido di unario '*' (avere 'long unsigned int') _Static_assert (* sizeof (my_arr) == 4, "");

Se è importante, sto usando gcc 5.3.0


15
Ho il sospetto che ( sizeof( my_arr ) )[ 0 ]fallisca.
Andrew Henle,

Un recente duplicato presenta un'altra variante di questa sorpresa di sintassi: perché compilare sizeof (x) ++?
Peter Cordes

Risposte:


197

sizeofnon è una funzione. È un operatore unario come !o ~.

sizeof(my_arr)[0]analizza come sizeof (my_arr)[0], che è solo sizeof my_arr[0]con parentesi ridondanti.

Questo è proprio come l' !(my_arr)[0]analisi !(my_arr[0]).

In generale, gli operatori postfix hanno una precedenza maggiore rispetto agli operatori prefissi in C. sizeof *a[i]++analizza come sizeof (*((a[i])++))(gli operatori postfix []e ++vengono applicati aprima agli operatori, quindi agli operatori prefisso *e sizeof).

(Questa è la versione dell'espressione di sizeof. Esiste anche una versione di tipo, che prende un nome di tipo tra parentesi:. sizeof (TYPE)In tal caso, le parentesi verrebbero richieste e parte della sizeofsintassi.)


14
Sapevo sicuramente che sizeof era un operatore unario e non una funzione, ma che avevo completamente dimenticato. Woops. Grazie per la spiegazione dettagliata. Il fatto che [] abbia una precedenza maggiore di * è interessante a prescindere.
bgomberg,

@melpomene Interessante. Non ho mai pensato di sizeofessere un operatore unario.
tastiera mutant

5
Non intendi "... parses as sizeof (my_arr[0])"? Il solo fatto di aggiungere uno spazio non cambia nulla.
Bernhard Barker,

Consiglierei sizeof((my_array)[0])invece
bolov,


46

sizeofha due "versioni": sizeof(type name)e sizeof expression. Il primo richiede un paio di ()argomenti. Ma quest'ultimo - quello con un'espressione come argomento - non ha ()attorno al suo argomento. Qualunque cosa ()tu usi nell'argomento è vista come parte dell'espressione dell'argomento, non come parte della sizeofsintassi stessa.

Poiché my_arrè noto al compilatore come un nome di oggetto, non un nome di tipo, sizeof(my_arr)[0]il compilatore viene effettivamente visto come sizeofapplicato a un'espressione:, sizeof (my_arr)[0]dove (my_arr)[0]è l'espressione dell'argomento. Il ()nome dell'array circostante è puramente superfluo. L'intera espressione è interpretata come sizeof my_arr[0]. Questo è equivalente al tuo precedente sizeof(my_arr[0]).

(Questo significa, a proposito, che il tuo precedente sizeof(my_arr[0])contiene anche una coppia di superfluo ().)

È un'idea sbagliata piuttosto diffusa che sizeofla sintassi in qualche modo richiede un paio di ()argomenti. Questo malinteso è ciò che induce in errore l'intuizione delle persone quando interpretano espressioni come sizeof(my_arr)[0].


1
La prima versione esiste in modo da poter controllare la dimensione di un numero intero in generale sulla macchina (dal retro quando non c'erano nemmeno macchine a 64 bit!), Ma intnon è un'espressione valida, quindi non è possibile utilizzare il seconda forma con esso.
NH.

25

[]hanno una precendenza più alta di sizeof. Quindi sizeof(my_arr)[0]è lo stesso di sizeof((my_arr)[0]).

Ecco un collegamento a una tabella di precedenza.


8

Stai utilizzando la versione sizeofdell'operatore che accetta un'espressione come parametro. A differenza di quello che accetta un tipo, non richiede parentesi. Quindi, l'operando è semplicemente (my_arr)[0], con le parentesi ridondanti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.