この記事でわかること
- 配列が0から始まる技術的起源はメモリアドレス計算の効率化
- 1982年のダイクストラ手書きメモ「Why numbering should start at zero」の論証
- C言語のarr[i]=*(arr+i)という設計が0-indexedを必然化させた歴史
- Fortran・Lua・MATLAB・R・Juliaが1-indexedを選んだ理由
- CVEバッファオーバーフローの約15%がOff-by-one起因というセキュリティ影響
- Rust・Python・Swiftが示す「安全に0から始める」現代的アプローチ
「なぜ配列のインデックスは0から始まるんですか?」——プログラミングを学び始めた人が必ず抱く疑問だ。日常生活では「1番目」から数えるのに、なぜコンピュータだけが「0番目」から始まるのか。この問いの答えは、1960年代のメモリ管理にまで遡る。
0-indexedの技術的起源
配列が0から始まる最大の理由は、メモリアドレスの計算にある。配列の要素にアクセスするとき、コンピュータは次の計算を行う。
要素のアドレス = 配列の先頭アドレス + (インデックス × 要素サイズ)
もしインデックスが0から始まれば、先頭要素のアドレスは「先頭アドレス + 0」となり、追加の計算が不要になる。1から始めると、毎回「-1」する引き算が発生する。1960年代のCPUにとって、この1回の引き算は無視できないコストだった。
| 言語 | 配列の開始インデックス | 設計年 | 理由 |
|---|---|---|---|
| C | 0 | 1972年 | ポインタ演算との一貫性 |
| Java | 0 | 1995年 | C/C++との互換性 |
| Python | 0 | 1991年 | C言語の慣習を継承 |
| Fortran | 1 | 1957年 | 数学の添え字表記に準拠 |
| Lua | 1 | 1993年 | 非プログラマー向けの直感性 |
| MATLAB | 1 | 1984年 | 行列計算の数学的慣習 |
ダイクストラの数学的論証
1982年、コンピュータ科学の巨人エドガー・ダイクストラは手書きのメモ「Why numbering should start at zero」を発表し、0-indexedの数学的な優位性を論じた。
ダイクストラの主張はこうだ。自然数の連続する部分列を表現する方法は4通りある。
| 表記法 | 2, 3, 4を表す場合 | ダイクストラの評価 |
|---|---|---|
| a ≤ i < b | 2 ≤ i < 5 | 最も優れている |
| a < i ≤ b | 1 < i ≤ 4 | 下限が不自然 |
| a ≤ i ≤ b | 2 ≤ i ≤ 4 | 空列の表現が醜い |
| a < i < b | 1 < i < 5 | 両端が不自然 |
「a ≤ i < b」の表記法では、部分列の長さが単純に「b - a」で求まる。さらに隣接する部分列を並べたとき、前の列の上限と次の列の下限が自然に一致する。この優雅さが、0-indexedの数学的根拠になっている。
C言語とポインタ——0-indexedが「必然」になった瞬間
0-indexedが単なる慣習ではなく技術的必然になったのは、1972年のC言語の登場がきっかけだ。C言語では配列とポインタが事実上等価であり、arr[i]は*(arr + i)の糖衣構文(シンタックスシュガー)に過ぎない。
この設計により、arr[0]は「arrの指すアドレスにある値」を意味し、arr[3]は「arrから3要素分進んだアドレスにある値」を意味する。もし1-indexedにすると、内部的に常に「-1」の計算が必要になり、ポインタ演算の美しい対応関係が崩れてしまう。
C言語がUNIXの実装言語として採用され、UNIXがコンピュータ科学の標準的なOS教育環境となったことで、C流の0-indexedは世界中のプログラマーにとっての「常識」になった。その後のC++、Java、JavaScript、Python、Go、Rust——主要な現代言語のほぼすべてがC言語の影響下で0-indexedを採用している。
「0 vs 1」論争——プログラマー文化の断層線
0-indexed vs 1-indexedの議論は、プログラマーコミュニティにおける一種の文化戦争でもある。Stack Overflowの2024年調査では、「配列は0から始まるべき」と回答したプログラマーが82%に達した。しかし、少数派の主張にも一理ある。
R言語の開発コミュニティでは、1-indexedの利点として「統計学者にとっての自然さ」が繰り返し強調されてきた。R言語のユーザーの多くは統計学者や生物学者であり、「第1サンプル」がインデックス1で参照できることは、コードの可読性を大幅に向上させる。
Juliaも1-indexedを採用した言語の一つだが、その理由は明快だ。Juliaは科学計算とデータサイエンスに特化しており、線形代数の教科書と一致する添え字表記が開発者体験を改善するからだ。Juliaの共同開発者Jeff Bezansonは「数学者にオフセットを強制するのは生産性の損失だ」と語っている。
1-indexedの言語はなぜ存在するのか
FortranやMATLABが1-indexedなのには正当な理由がある。これらの言語は数学者や科学者が主要ユーザーであり、数学の添え字表記(a₁, a₂, a₃...)と一致させることが最優先だった。
Luaが1-indexedを採用したのも興味深い。Luaはブラジルのリオデジャネイロ・カトリカ大学で生まれた言語で、プログラマーではなくエンジニアや地質学者が使うことを想定していた。彼らにとって「1番目の要素がインデックス1」であることは当然だった。
「Off-by-one error」——0-indexedが生む永遠のバグ
配列が0から始まることで、プログラマーは「Off-by-one error(OBOE)」という古典的なバグと永遠に戦い続けている。ループの終了条件を「<」にすべきか「<=」にすべきか——この判断ミスは、初心者からベテランまで等しく悩ませる。
あるセキュリティ研究では、CVE(共通脆弱性識別子)に登録されたバッファオーバーフローの約15%がOff-by-oneに起因するとされている。つまり0-indexedは効率を生む一方で、セキュリティリスクの源泉にもなっている。
現代の言語設計——0-indexedを超える試み
近年の言語設計では、0-indexed/1-indexedの二項対立を超えるアプローチも登場している。Rustは0-indexedを採用しつつも、コンパイル時の境界チェックによりバッファオーバーフローを原理的に防止する。Go言語のslice機構は、配列のビュー(部分参照)を安全に扱う方法を提供している。
Pythonのネガティブインデックス(arr[-1]で末尾要素を参照)は、0-indexedの延長線上にある独創的な工夫だ。arr[-1]は内部的にarr[len(arr) - 1]に変換されるが、この簡潔な表記はPythonの可読性の高さに大きく貢献している。
SwiftのArrayやRustのVec型では、インデックスアクセスの安全性が言語レベルで保証されている。範囲外アクセスはコンパイルエラーまたは実行時パニック(クラッシュ)として処理され、C言語のような未定義動作は発生しない。メモリ安全性を犠牲にして効率を取った時代の終わりを象徴する設計だ。
こうした現代の工夫を見ると、0-indexedの本質的な問題はインデックスの開始位置そのものではなく、境界違反時の挙動にあったことがわかる。問題を解決するアプローチは「1から始める」ことではなく「安全に0から始める」ことだったのだ。
「正しさ」と「わかりやすさ」のトレードオフ
SwiftやKotlinのような現代の言語でも、配列は0から始まる。ハードウェアの制約がほぼ消えた現代でも0-indexedが主流なのは、C言語から続く巨大なエコシステムとの互換性と、ダイクストラが示した数学的な整合性が支持されているからだ。
「なぜ0から始まるのか」という問いには、効率・歴史・数学・人間工学のすべてが交差している。プログラミング教育の現場では、0-indexedの「なぜ」を教えるかどうかが議論の的になっている。多くの入門書は「そういうものだ」と暗黙の了解として扱い、背景を説明しない。しかし、歴史的・数学的な理由を理解することで、配列操作のバグを直感的に防げるようになるという研究もある。
次に配列を宣言するとき、あなたはその[0]に1960年代のメモリ管理の遺産を見るだろうか——それとも、ただ「最初の要素」と読むだろうか。
日常の観察が思考を育てる
小さな気づきを言葉にする習慣は、思考の筋肉を鍛える最も効果的な方法の一つだ。
見過ごされがちな日常の断片に目を留め、自分の言葉で表現し直す。
この繰り返しが、いずれ仕事の判断や人との対話にも効いてくる。
次に何かに違和感を覚えたら、その瞬間を書き留めてみる価値があるかもしれない。
よくある質問(FAQ)
Q. 配列が0から始まる技術的な理由は?
メモリアドレス計算を効率化するためです。
要素のアドレス=先頭アドレス+(インデックス×要素サイズ)という式で、インデックスが0なら先頭要素は「先頭アドレス+0」で済みます。
1960年代のCPUにとって、1-indexedで毎回発生する引き算コストは無視できませんでした。
Q. ダイクストラは何を主張したのか?
1982年の手書きメモで、自然数の連続する部分列を表す4通りの表記法のうち「a ≤ i < b」が最も優れていると論証しました。
部分列の長さが単純に「b-a」で求まり、隣接する部分列の境界が自然に一致する、という数学的な優雅さが0-indexedの根拠になっています。
Q. なぜFortranやLua、R、Juliaは1-indexedなのか?
主要ユーザーが数学者・統計学者・科学者であり、数学の添え字表記(a₁, a₂, a₃...)との一致を優先したからです。
Luaはブラジルのエンジニアや地質学者向け、Juliaは科学計算・データサイエンス向けに設計され、Jeff Bezansonは「数学者にオフセットを強制するのは生産性の損失」と語っています。
Q. 0-indexedが引き起こす問題はあるのか?
Off-by-one error(OBOE)です。
ループの終了条件を「<」にすべきか「<=」にすべきかの判断ミスは初心者からベテランまで等しく悩ませ、CVE登録のバッファオーバーフローの約15%がOff-by-oneに起因するとされています。
効率と引き換えにセキュリティリスクの源泉にもなっているのです。
Q. 現代の言語はどう解決しているのか?
0-indexedを維持しつつ境界違反時の挙動を安全化する方向です。
Rustはコンパイル時の境界チェック、Pythonはarr[-1]で末尾参照できるネガティブインデックス、Swift・Rustのコレクション型は範囲外アクセスを言語レベルで検出します。
「1から始める」のではなく「安全に0から始める」ことが現代的な解です。
よくある質問
Q1. なぜ配列は0から始まるのか?
メモリアドレス計算の効率化が起源である。先頭アドレスにインデックスを足すだけで要素位置が求まるため、1始まりで毎回引き算するコストを省ける。1960年代のCPU性能では無視できない差だった。
Q2. ダイクストラの主張の核心は?
半開区間「a ≤ i < b」が最も自然だという論証である。空列を破綻なく表現でき、長さがb-aで直感的に求まる。1982年の手書きメモで4通りの記法を比較し、0始まり半開区間の優位を示した。
Q3. Off-by-oneエラーは深刻か?
無視できないリスクである。CVEバッファオーバーフローの約15%がOff-by-one起因とされ、境界条件の取り違いが脆弱性につながる。Rust等の現代言語は安全な反復子で構造的に防ぐ設計を採る。