このコードがコンパイルされるのはなぜですか?
_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を使用しています
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
構文の一部です。 )
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]
として解釈するときに人々の直感を誤解させるものです。
[]
は、sizeof
よりも優先順位が高くなっています。したがって、sizeof(my_arr)[0]
はsizeof((my_arr)[0])
と同じです。
ここ は、優先順位テーブルへのリンクです。
式をパラメーターとしてとるsizeof
演算子のバージョンを使用しています。型を取るものとは異なり、does n'tは括弧を必要とします。したがって、オペランドは単純に(my_arr)[0]
であり、括弧は冗長です。