プログラミングを始めたばかりの人が最初に感じる違和感、それが「なぜ最初の要素が 1 番目ではなく 0 番目なのか?」という疑問です。 日常の感覚とはズレていますが、コンピュータの世界では 0 から始めることには、数学的にも構造的にも非常に強力なメリットがあります。

その理由を「メモリアドレス」と「計算の美しさ」の観点から紐解いてみます。

1. インデックスは「距離(オフセット)」である

これが最も本質的な理由です。 配列は、メモリ上の「連続した箱」の中にデータが並んでいます。コンピュータが特定の要素の場所を探すとき、裏側では次のような計算を行っています。

要素のアドレス = 先頭のアドレス + (インデックス × 要素のサイズ)

もし配列の先頭を 1 から始めると、この計算は毎回こうなります。

要素のアドレス = 先頭のアドレス + ((インデックス - 1) × 要素のサイズ)

毎回 - 1 をしなきゃいけない。 これはコンピュータにとって無駄な処理ですし、0 から始めれば「先頭のアドレス + 距離(インデックス)」という非常にシンプルな足し算だけで済みます。つまり、インデックス 0 は「先頭からの距離が 0」であることを意味しているのです。

2. 範囲の表現がスマートになる

0 始まりの配列において、長さ n の配列の範囲を表現すると、数学的に非常に綺麗になります。
  • インデックスの範囲:0 <= i < n
  • スライスなどの範囲:[start, end)(start は含み、end は含まない)
このとき、「要素数 = end - start」 という単純な引き算で求まります。 もし 1 始まりだと、1 <= i <= n となり、引き算のあとに + 1 をしないと要素数が合いません。アルゴリズムを書く際、この「プラス 1」の有無がバグ(オフバイワンエラー)の原因になりやすいため、0 始まりの方が安全だと言えます。

3. 歴史的な継承(C 言語の影響)

現代の多くの言語(Java, Python, JavaScript など)が 0 始まりなのは、それらが C 言語 の設計を色濃く継承しているからです。

C 言語では、配列のインデックス指定 a[i] は、ポインタ演算 *(a + i) の書き換え(シンタックスシュガー)に過ぎません。ポインタの足し算そのものが「先頭からのオフセット」を意味していたため、0 始まりが必然でした。

この設計があまりに合理的だったため、後発の言語もこれに従うのがデファクトスタンダードとなりました。

まとめ

「1 から数える」のは人間にとって自然ですが、「0 からオフセットを計算する」のはコンピュータにとって自然です。

  • 0 は「先頭からの距離」を意味する
  • 範囲計算の引き算が綺麗に決まる
  • C 言語からの合理的な設計の継承
次に array[0] と書くときは、これがメモリ上の先頭アドレスをダイレクトに指し示している「距離 0」の場所なんだな、と思い浮かべてみてください。