関連記事
同じ「テック雑学」シリーズで、こちらも読まれています。
当サイトでは、サービス改善のためにCookieを使用しています。詳しくはプライバシーポリシーをご覧ください。
「なぜ配列のインデックスは0から始まるんですか?」——プログラミングを学び始めた人が必ず抱く疑問だ。日常生活では「1番目」から数えるのに、なぜコンピュータだけが「0番目」から始まるのか。この問いの答えは、1960年代のメモリ管理にまで遡る。
配列が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の数学的根拠になっている。
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-indexed vs 1-indexedの議論は、プログラマーコミュニティにおける一種の文化戦争でもある。Stack Overflowの2024年調査では、「配列は0から始まるべき」と回答したプログラマーが82%に達した。しかし、少数派の主張にも一理ある。
R言語の開発コミュニティでは、1-indexedの利点として「統計学者にとっての自然さ」が繰り返し強調されてきた。R言語のユーザーの多くは統計学者や生物学者であり、「第1サンプル」がインデックス1で参照できることは、コードの可読性を大幅に向上させる。
Juliaも1-indexedを採用した言語の一つだが、その理由は明快だ。Juliaは科学計算とデータサイエンスに特化しており、線形代数の教科書と一致する添え字表記が開発者体験を改善するからだ。Juliaの共同開発者Jeff Bezansonは「数学者にオフセットを強制するのは生産性の損失だ」と語っている。
FortranやMATLABが1-indexedなのには正当な理由がある。これらの言語は数学者や科学者が主要ユーザーであり、数学の添え字表記(a₁, a₂, a₃...)と一致させることが最優先だった。
Luaが1-indexedを採用したのも興味深い。Luaはブラジルのリオデジャネイロ・カトリカ大学で生まれた言語で、プログラマーではなくエンジニアや地質学者が使うことを想定していた。彼らにとって「1番目の要素がインデックス1」であることは当然だった。
配列が0から始まることで、プログラマーは「Off-by-one error(OBOE)」という古典的なバグと永遠に戦い続けている。ループの終了条件を「<」にすべきか「<=」にすべきか——この判断ミスは、初心者からベテランまで等しく悩ませる。
あるセキュリティ研究では、CVE(共通脆弱性識別子)に登録されたバッファオーバーフローの約15%がOff-by-oneに起因するとされている。つまり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年代のメモリ管理の遺産を見るだろうか——それとも、ただ「最初の要素」と読むだろうか。
小さな気づきを言葉にする習慣は、思考の筋肉を鍛える最も効果的な方法の一つだ。
見過ごされがちな日常の断片に目を留め、自分の言葉で表現し直す。
この繰り返しが、いずれ仕事の判断や人との対話にも効いてくる。
次に何かに違和感を覚えたら、その瞬間を書き留めてみる価値があるかもしれない。
メモリアドレス計算を効率化するためです。
要素のアドレス=先頭アドレス+(インデックス×要素サイズ)という式で、インデックスが0なら先頭要素は「先頭アドレス+0」で済みます。
1960年代のCPUにとって、1-indexedで毎回発生する引き算コストは無視できませんでした。
1982年の手書きメモで、自然数の連続する部分列を表す4通りの表記法のうち「a ≤ i < b」が最も優れていると論証しました。
部分列の長さが単純に「b-a」で求まり、隣接する部分列の境界が自然に一致する、という数学的な優雅さが0-indexedの根拠になっています。
主要ユーザーが数学者・統計学者・科学者であり、数学の添え字表記(a₁, a₂, a₃...)との一致を優先したからです。
Luaはブラジルのエンジニアや地質学者向け、Juliaは科学計算・データサイエンス向けに設計され、Jeff Bezansonは「数学者にオフセットを強制するのは生産性の損失」と語っています。
Off-by-one error(OBOE)です。
ループの終了条件を「<」にすべきか「<=」にすべきかの判断ミスは初心者からベテランまで等しく悩ませ、CVE登録のバッファオーバーフローの約15%がOff-by-oneに起因するとされています。
効率と引き換えにセキュリティリスクの源泉にもなっているのです。
0-indexedを維持しつつ境界違反時の挙動を安全化する方向です。
Rustはコンパイル時の境界チェック、Pythonはarr[-1]で末尾参照できるネガティブインデックス、Swift・Rustのコレクション型は範囲外アクセスを言語レベルで検出します。
「1から始める」のではなく「安全に0から始める」ことが現代的な解です。
メモリアドレス計算の効率化が起源である。先頭アドレスにインデックスを足すだけで要素位置が求まるため、1始まりで毎回引き算するコストを省ける。1960年代のCPU性能では無視できない差だった。
半開区間「a ≤ i < b」が最も自然だという論証である。空列を破綻なく表現でき、長さがb-aで直感的に求まる。1982年の手書きメモで4通りの記法を比較し、0始まり半開区間の優位を示した。
無視できないリスクである。CVEバッファオーバーフローの約15%がOff-by-one起因とされ、境界条件の取り違いが脆弱性につながる。Rust等の現代言語は安全な反復子で構造的に防ぐ設計を採る。
同じ「テック雑学」シリーズで、こちらも読まれています。
ニュースを追うより、変化をつかむ。
毎週月曜、読むべきものだけを3分で。いつでも解除できます。
CPUの話で思い出すのは、深圳でのフィールドワーク中に見た光景。技術の使われ方は文化によって本当に異なる。 なぜプログラミングの配列は0から始まるのかを日本のコンテキストで考えると、C01972も含めて独特の受容パターンが見えてくる。 数字では捉えきれない「手触り」の部分にこそ、文化人類学の出番がある。
CPUの話を読みながら考えたのは、テクノロジーの進歩と人間の幸福は必ずしも正比例しないということ。 なぜプログラミングの配列は0から始まるのかは効率や便利さの次元では明らかに進歩だが、哲学的に言えば「進歩」の定義そのものが問い直される局面にある。 C01972の存在も含めて、私たちは技術と共にどう生きたいのか。その問いは、各自が引き受けるしかない。
CPU、個人的にかなり興味あって追いかけてるテーマ。 なぜプログラミングの配列は0から始まるのかに関しては、研究室の先輩たちの間でも「これは来る」って言われてて、納得。 C01972のアプローチも面白いし、自分たちの世代がこの領域でどう貢献できるかを考えると、ワクワクする。まずは手を動かしてみるのが一番。
※ 一部のコメントはAIが記事内容を分析し、専門家の視点をシミュレーションして生成したものです。