C++ クイズの解説

先日、こんな投稿があり、C++ 界隈で話題だったようです。

short a = 1;
std::cout << sizeof(+a)["23456"] << std::endl;

上のコードで表示されるのはいったい何かという C++ のクイズです。
奇怪なコードですが警告やエラーにはなりません。
多分 "Without checking" で正解できた人はかなり少ないのではないかと思うのですが……。

正解

正解は 1 が表示されます。

解説

まず、a[b]b[a]と同じです。
これは何故かというと、a[b]*(a + b)シンタックスシュガー*1に過ぎず、また二項の + 演算子は交換法則が成り立つので*(a + b)*(b + a)と同じになるからです。

// 全部同じ
"hoge"[1];
*("hoge" + 1);
*(1 + "hoge");
1["hoge"];

ちなみに文字列リテラルの値は、その文字列の先頭へのポインタになります。

const char* str = "hoge";

// 全部同じ
str[1];
*(str + 1);
*(1 + str);
1[str];

上のように書けばわかりやすいのではないでしょうか。

次に、+aは値としての変化はないのですが、暗黙の型変換により short 型から int 型に格上げされます。
ただしこれはひっかけで、答えには影響を及ぼしません。

そして、sizeof 演算子について。
指定された式または型のバイト単位のサイズを返す演算子ですが、オペランド式の場合の括弧はなくても構いません

sizeof(int);  // 括弧が必要
sizeof 0;  // 括弧がなくてもOK

また、sizeof 演算子よりも添字演算子の方が優先順位が高いため、sizeof(a)[b]sizeof((a)[b])と同じです。
ここを(sizeof(a))[b]であると無意識に勘違いしてしまった人は多そうです。見事に勘違いしてました。

というわけで、sizeof(+a)["23456"]という式がどう解釈されるかというと、

sizeof(+a)["23456"];
sizeof("23456"[+a]);
sizeof("23456"[1]);
sizeof('3');

となり、char 型のサイズを返すことになり 1 が表示されるというわけでした。*2

ちなみに筆者の思考回路では

sizeof(+a)["23456"];
sizeof(short)["23456"];  // +a が int になることを忘れ…
2["23456"];  // 優先順位に気が付かず…
"23456"[2];
'4'

4 になるのかと思いました。

*1:構文糖衣。意味は変わらないけど簡単に書けるようにする記法。(*p).m が p->m と書けるのもシンタックスシュガー

*2:C では文字リテラルは int になるのでこの書き方だと語弊がありますが、最終的にはC だろうが C++ だろうが sizeof(char) と評価されることに変わりはありません