Root One

数学中心のブログです。

C++で驚いた話

最近C++をさわりはじめたのですが、

すこし驚いたことがあったので、メモを残しておきたいと思います。

少し驚いたコード

vector<int> v = { 1,2,3 };
for (int i = 0; i < v.size() - 4; i++) {
 cout << v[i] << endl;
 if (i == 2) break;
}

実行結果.

1
2
3

何が意外だったのか

v.size()は3なので、v.size()-4 は -1 になるように見えます。

したがって、for文の中は実行されず、何も表示されないはずです。

しかし、実行結果を見ると、中身が実行されているので

何かおかしい

となったわけです。

理由は簡単で、

v.size() は size_t 型(unsigned long long ) になってしまい、

v.size()-4 もsize_t 型になって、 size_t 型の世界の -1 と解釈されたせいです。

size_t 型の世界で -1 は、負の数を扱うことができないので、

エラーになっても良さそうですが、そうはならず、

size_t 型が扱える一番大きな数として認識されてしまうようです。

(実験した環境では2^{64}-1になりました。)

つまり、結果的に上のコードは

vector<int> v = { 1,2,3 };
for (int i = 0; i < 18446744073709551615; i++) {
 cout << v[i] << endl;
 if (i == 2) break;
}

と解釈されて実行されたということになります。

偶然ですが、この話は、まさに10adicの世界で

\[-1 = \cdots 99999 \]

が成り立つというお話ですね。 

日常生活でp-adic的な現象に出会うことはあまりないかもしれませんが、

プログラムの世界だとこのようにp-adicの世界が見え隠れすることがあるようです。

追記

上記のようなことがあるので、size_t 型から引き算するときは充分注意しなければならないようです。

そして問題は「どのように回避すべきか」ということになりますが、次のように事前に int にキャストすれば良いようです。

vector v = { 1,2,3 };
for (int i = 0; i < static_cast<int>(v.size()) - 4; i++) {
	cout << v[i] << endl;
	if (i == 2) break;
}

実行結果. (何も表示されない.)