Mengapa sizeof (my_arr) [0] mengkompilasi dan sizeof sama (my_arr [0])?

129

Mengapa kode ini dikompilasi?

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

2 menegaskan pertama jelas benar, tetapi saya akan berharap baris terakhir gagal, karena pemahaman saya adalah yang sizeof()harus mengevaluasi ke literer integer, yang tidak dapat diperlakukan sebagai array. Dengan kata lain, itu akan gagal dengan cara yang sama seperti baris berikut gagal:

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

Menariknya, yang berikut ini memang gagal dikompilasi (yang seharusnya melakukan hal yang sama, bukan?):

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

galat: argumen tipe tidak valid dari '*' unary (memiliki 'int unsigned lama') _Static_assert (* sizeof (my_arr) == 4, "");

Jika itu penting, saya menggunakan gcc 5.3.0

bgomberg
sumber
15
Saya kira ( sizeof( my_arr ) )[ 0 ]gagal.
Andrew Henle
Duplikat terbaru memiliki variasi lain pada kejutan sintaksis ini: Mengapa sizeof (x) ++ dikompilasi?
Peter Cordes

Jawaban:

197

sizeofbukan fungsi. Ini operator seperti unary !atau ~.

sizeof(my_arr)[0]mem-parsing as sizeof (my_arr)[0], yang hanya sizeof my_arr[0]dengan kurung redundan.

Ini seperti !(my_arr)[0]mem - parsing seperti !(my_arr[0]).

Secara umum, operator postfix memiliki prioritas lebih tinggi daripada operator prefix di C. sizeof *a[i]++parses as sizeof (*((a[i])++))(operator postfix []dan ++diterapkan aterlebih dahulu, kemudian operator prefix *dan sizeof).

(Ini adalah versi ekspresi sizeof. Ada juga versi tipe, yang mengambil nama tipe tanda kurung:. sizeof (TYPE)Dalam hal ini parens akan diperlukan dan bagian dari sizeofsintaks.)

melpomene
sumber
14
Saya benar-benar tahu bahwa sizeof adalah operator unary dan bukan fungsi, tetapi benar-benar lupa. Aduh. Terima kasih untuk penjelasan rinci. Fakta bahwa [] lebih diutamakan daripada * adalah menarik terlepas.
bgomberg
@melpomene Menarik. Saya tidak pernah berpikir untuk sizeofmenjadi operator unary.
mutantkeyboard
5
Bukankah maksud Anda "... parses as sizeof (my_arr[0])"? Menambahkan ruang saja tidak benar-benar mengubah apa pun.
Bernhard Barker
Saya akan merekomendasikan sizeof((my_array)[0])sebagai gantinya
bolov
46

sizeofmemiliki dua "versi": sizeof(type name)dan sizeof expression. Yang pertama membutuhkan sepasang ()argumen. Tapi yang terakhir - yang berekspresi sebagai argumen - tidak ada di ()sekitar argumennya. Apa pun yang ()Anda gunakan dalam argumen dipandang sebagai bagian dari ekspresi argumen, bukan bagian dari sizeofsintaks itu sendiri.

Karena my_arrdiketahui kompiler sebagai nama objek, bukan nama tipe, Anda sizeof(my_arr)[0]sebenarnya dilihat oleh kompiler sebagaimana sizeofditerapkan pada ekspresi:, di sizeof (my_arr)[0]mana (my_arr)[0]ekspresi argumen. Nama ()array di sekelilingnya murni berlebihan. Seluruh ekspresi diartikan sebagai sizeof my_arr[0]. Ini sama dengan sebelumnya sizeof(my_arr[0]).

(Ini berarti, BTW, bahwa sebelumnya Anda sizeof(my_arr[0])juga berisi sepasang berlebihan ().)

Ini adalah kesalahpahaman yang agak luas bahwa sizeofsintaksis entah bagaimana memerlukan sepasang ()argumen. Kesalahpahaman ini adalah yang menyesatkan intuisi orang ketika menafsirkan ekspresi seperti itu sizeof(my_arr)[0].

Semut
sumber
1
Versi pertama ada sehingga Anda dapat memeriksa ukuran integer secara umum pada mesin (dari belakang ketika bahkan tidak ada mesin 64-bit!), Tetapi intbukan ekspresi yang valid, sehingga Anda tidak dapat menggunakan bentuk kedua dengan itu.
NH.
25

[]memiliki prioritas lebih tinggi dari sizeof. Begitu sizeof(my_arr)[0]juga dengan sizeof((my_arr)[0]).

Berikut ini tautan ke tabel prioritas.

mch
sumber
8

Anda menggunakan versi sizeofoperator yang menggunakan ekspresi sebagai parameter. Berbeda dengan yang mengambil jenis, itu tidak memerlukan tanda kurung. Oleh karena itu, operannya sederhana (my_arr)[0], dengan tanda kurung menjadi berlebihan.

Quentin
sumber