147
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Twitterで話題のC++ quiz time!の解説を誰も書かないので書く

Last updated at Posted at 2019-03-21

問題

こんなツイートが人気を博していた。

#include <iostream>
#include "typename.h"
int main()
{
    short a = 1;
    std::cout << sizeof(+a)["23456"] << std::endl;
}

答えは四択で1,3,4,6のどれですか?というもの。

だれか解説書くだろと思ったが、みんなTwitter上で満足してしまったらしい(見落としてたらすまん)。解説するか。

正解

1になります。

・・・えええっ!?

(私は6だと騙された)

6と誤答した人の脳内回路

誤答したの私だ。アンケートを見ても一番多い。

江添さんがノリノリで誤答を書き込んでいる。

sizeof(+a)["23456"]をどうパースしたかというと

  1. +a
  2. sizeof(+a)
  3. sizeof(+a)["23456"]

とパースしてしまったのだと思う。一般にsizeof演算子は必ず()とつけて使いましょう、と多くの初心者本にかかれており、そして実際多くのC++erが()をつけて使うもんだから、うっかりsizeof(+a)とパースしてしまいそうになるが、operator[]の優先順位がめっちゃ高いことを忘れないでほしい(ブーメラン)

  1. 暗黙変換でintになるよね~
  2. sizeof取ったら4でしょ?
  3. a[b]b[a]とも書けるから文字列の4番めの要素は6

と考えていく。

不正解だ

4と誤答した人の脳内回路

6とほぼ同じだが+aが暗黙変換でintになることを見落とすと4になる

  1. shortでしょ?
  2. sizeof取ったら2でしょ?
  3. a[b]b[a]とも書けるから文字列の4番めの要素は4

と考えていく。

不正解だ

3と誤答した人の脳内回路

ごめんなさい、さっぱりわからない。アンケートでももっとも票数が少ないものの、殆ど無いってわけでもないくらい投票されている。

short a = 1;

だからよくわからんけど文字列の1番めの要素を見に行ってしまったとか??

解説

+a

改めて見ていこう。

short a = 1;

なのでaの型はshort型だ。では+aの型は?

実はint型になる。

単項演算子+の演算の際にintegral promotionという暗黙変換が行われるためだ。

まあ実はint型であるかは重要ではない

(+a)["23456"]

そうだ、ここで誤ってsizeof(+a)を考えてしまうと誤りだ。

operator[]はポインタ演算*(a + b)のsyntax sugarであるというのは割と有名な小話だと思う。

int arr[3] = { 1, 2, 3 };
arr[1]; // => 2
1[arr]; // => 2
*(arr + 1); // => 2

つまりこの場合

(+a)["23456"]"23456"[+a]は等価だ。

aの値は

short a = 1;

より1なので演算結果はchar型の3になる。

sizeof(+a)["23456"]

ようやくsizeof演算子の出番だ。(+a)["23456"]はchar型なので、sizeofを取ると1になる。

sizeof((+a)["23456"])となっていれば引っかかる人は少なかっただろうが・・・。

余談

@Chironian 氏御用達の型名を表示させるマクロTYPENAMEを使いつつ、問題のコードをいじると理解度が増すかもしれない。

#include <iostream>
#include "typename.h"
int main()
{
    short a = 1;
    std::cout
        << sizeof(+a) << std::endl
        << "23456"[4] << std::endl
        << 4["23456"] << std::endl
        << (+a)["23456"] << std::endl
        << TYPENAME((+a)["23456"]) << std::endl
        << sizeof(+a)["23456"] << std::endl;
}

実行結果
4
6
6
3
char
1

教訓

  • sizeof演算子を使うときは()でくくりましょう
  • operator[]の優先順位の高さを忘れない

感想

まんまとひっかかった、くやしい。

License

CC BY 4.0

CC-BY icon.svg

147
63
10

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
147
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?