web-dev-qa-db-ja.com

sizeof(my_arr)[0]がコンパイルされ、sizeof(my_arr [0])と等しいのはなぜですか?

このコードがコンパイルされるのはなぜですか?

_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つのアサートは明らかに正しいですが、私の理解では、sizeof()は整数リテラルに評価されるべきであり、配列として扱うことができないため、最後の行は失敗します。つまり、次の行が失敗するのと同じように失敗します。

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

興味深いことに、以下は実際にコンパイルに失敗します(これは同じことをしているはずですよね?):

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

エラー:単項 '*'の無効な型引数( 'long unsigned int'を持つ)_Static_assert(* sizeof(my_arr)== 4、 "");

問題があれば、gcc 5.3.0を使用しています

126
bgomberg

sizeofは関数ではありません。 !~のような単項演算子です。

sizeof(my_arr)[0]は、sizeof (my_arr)[0]として解析されます。これは、冗長な括弧を付けたsizeof my_arr[0]です。

これは、!(my_arr)[0]!(my_arr[0])として解析するのと同じです。

一般に、後置演算子はCの前置演算子よりも優先されます。sizeof *a[i]++sizeof (*((a[i])++))として解析されます(後置演算子[]および++は最初にaに適用されます、次にプレフィックス演算子*およびsizeof)。

(これはsizeofの式バージョンです。括弧付きの型名をとる型バージョンもあります:sizeof (TYPE)。その場合、括弧は必須で、sizeof構文の一部です。 )

192
melpomene

sizeofには、sizeof(type name)sizeof expressionの2つの「バージョン」があります。前者は、引数の周りに()のペアが必要です。しかし、後者-引数として式を持つもの-は、引数の周りに()がありません。引数で使用する()は、sizeof構文自体の一部ではなく、引数式の一部と見なされます。

my_arrは型名ではなくオブジェクト名としてコンパイラーに認識されるため、実際にsizeof(my_arr)[0]は式に適用されるsizeofとしてコンパイラーに認識されます:sizeof (my_arr)[0]、ここで、(my_arr)[0]は引数式です。配列名を囲む()は純粋に不要です。式全体はsizeof my_arr[0]として解釈されます。これは以前のsizeof(my_arr[0])と同等です。

(つまり、以前のsizeof(my_arr[0])には余分な()のペアも含まれているということです。)

sizeofの構文には、引数の周りに()のペアが何らかの形で必要であるという誤解が広まっています。この誤解は、そのような表現をsizeof(my_arr)[0]として解釈するときに人々の直感を誤解させるものです。

45
AnT

[]は、sizeofよりも優先順位が高くなっています。したがって、sizeof(my_arr)[0]sizeof((my_arr)[0])と同じです。

ここ は、優先順位テーブルへのリンクです。

24
mch

式をパラメーターとしてとるsizeof演算子のバージョンを使用しています。型を取るものとは異なり、does n'tは括弧を必要とします。したがって、オペランドは単純に(my_arr)[0]であり、括弧は冗長です。

7
Quentin