web-dev-qa-db-ja.com

ゼロベースの配列がなぜ標準であるのですか?

A ここで質問 は、他のプログラマーとの議論を思い出しました。彼は、ゼロベースの配列は1ベースの配列に置き換える必要があると主張しました。これは、ゼロベースの配列が、配列とポインター、およびコンピューターハードウェアの動作に由来する実装の詳細であるためです。ただし、このようなものは、より高いレベルに反映されるべきではありません。言語。

今は私は議論があまり得意ではないので、ゼロベースの配列を使用する理由として、より適切であると感じられる理由以外は何も説明できませんでした。なぜisが配列の一般的な開始点をゼロにするのですか?

112
Tamas Czinege

Edsger W. Dijkstraの記事より強力な議論を提供できる人は誰もいないと思います "番号付けはゼロから始める必要がある理由"

105
Bill the Lizard

権限の議論

ええと...どうやら、ごく最近のものを含むほとんどの言語はゼロベースです。それらの言語はかなり熟練した人々によって書かれたので、あなたの友人は間違っているに違いありません...

なんで?

なぜ1がゼロよりも良い開始インデックスになるのですか?なぜ2または10ではないのですか?答え自体は興味深いです。アイデアを擁護する人々の徹底的なプロセスについて多くを示しているからです。

の 最初 議論はそれがより自然であるということです、なぜなら 第一 通常は 1 他のすべての人の前に、少なくとも、大多数の人々にとって...

番号-1 引数は、最後のインデックスも配列のサイズであることです...

この種の議論について私が通常聞く理由の「質」にはまだ感銘を受けています...そして、私がそれを思い出したときはなおさらです...

なぜゼロではないのですか?

...「1を基にした」表記は、西洋の文化の残されたものであり、何世紀にもわたってゼロの存在を無視していたものです。

信じられないかもしれませんが、元のグレゴリオ暦は-3、-2、-1、1、2、3です...それが西洋科学に寄与した問題(たとえば、1月1日からの年数-2)を想像してみてください1月1日1日まで、元のグレゴリオ暦が減算と同じくらい単純なものと競合することを確認してください...)。

1ベースの配列を維持することは(まあ、私はそのためにダウンモッド化します... ^ _ ^ ...)、21世紀のマイルとヤードを維持するようなものです...

なぜゼロなのか?数学だから!

最初 (OOops ...すみません...私はもう一度やってみます)

ゼロ、ゼロは何もない、1つは何か。そして、いくつかの宗教的テキストは「最初は何もなかった」としています。一部のコンピューター関連の議論は、宗教的な論争のように燃えている可能性があるので、この点は思われるほどトピックから外れることはありません... ^ _ ^

最初、0ベースの配列を使用してその0番目の値を無視する方が、1ベースの配列を使用してハッキングしてその0番目の値を見つけるよりも簡単です。この理由は、以前とほぼ同じくらい愚かですが、1ベースの配列を支持する最初の議論もかなり誤りでした。

二番目、数値を扱う場合、可能性が高いことを思い出してみましょう。数学を扱う場合、可能性が高いので、時代遅れの慣習を回避する愚かなハックの気分ではありません。 Oneベースの表記法は、何世紀にもわたって数学と日付を悩ませてきました。私たちの過ちから学ぶことによって、将来の指向科学(コンピューター言語を含む)でそれを回避するよう努めるべきです。

第三、ハードウェアに関連付けられているコンピューター言語の配列については、21の整数のC配列を割り当て、ポインターを10のインデックスを右に移動すると、自然な[-10 to 10]配列になります。これはハードウェアにとって自然なことではありません。しかし、それは数学のためのものです。もちろん、数学は時代遅れかもしれませんが、私が最後にチェックしたとき、世界中のほとんどの人はそれがそうではないと信じていました。

、すでに他の場所で指摘されているように、離散位置(または距離が離散値に減少)の場合でも、最初のインデックスはゼロであり、建物の床(ゼロから開始)のように、減少するカウントダウン(3、2、1、ゼロ! )、地上高度、画像の最初のピクセル、温度(絶対零度の場合はゼロケルビン、または氷点下273 Kの摂氏ゼロ)。実際、本当に1つから始まるのは、「最初、 二番目、 第三、など」 反復 表記は、私を自然に  ポイント...

 の  ポイント(当然のことながら、 )は、高レベルのコンテナにアクセスする必要があることです。インデックスではなく、 イテレータ、インデックス自体に固有の値がない限り。あなたの「高級言語」の擁護者がそれについて言及しなかったことに驚いています。インデックス自体が重要な場合は、数学に関連する質問をする時間の半分を賭けることができます。したがって、コンテナは数学に優しく、1から始まる「あなたの古いグレゴリオ暦」のように数学を無効にせず、それを機能させるために逆ハックが必要です。

結論

本来プログラマーがぼやけている話し言葉や書き言葉の習慣を、コンピューター言語(命令をぼやけさせたくない)に不必要に結び付け、ハードウェアを誤って原因とするため、他のプログラマーからの議論は誤りです。この問題の理由として、彼は、言語の抽象化が進むにつれて、ゼロベースの配列は過去のものであるとあなたに納得させたいと考えています。

ゼロベースの配列は、数学に関連する理由によりゼロベースです。 ハードウェア関連の理由ではありません。

さて、これがあなたの仲間のプログラマにとって問題であるなら、彼にイテレータやforeachループのような本当の高レベルの構造体でプログラミングを始めさせてください。

80
paercebal

ハーフオープン間隔はうまく構成されます。0 <= i < limを処理していて、n要素で拡張したい場合、新しい要素はlim <= i < lim + nの範囲のインデックスを持ちます。ゼロベースの配列での作業算術が容易になります分割または連結配列の場合、または要素のカウントの場合。より単純な計算がfencepostエラーが少ないにつながることを願っています。

47
Norman Ramsey

特定のタイプの配列操作は、1ベースの配列では非常に複雑になりますが、0ベースの配列ではより単純です。

ある時点で数値解析プログラミングを行いました。私はアルゴリズムを使用して、FORTRANとC++の両方で書かれた圧縮された疎行列を操作していました。

FORTRANアルゴリズムにはたくさんのa[i + j + k - 2]、C++にはa[i + j + k]、C++配列は0ベースでしたが、FORTRAN配列は1ベースでした。

29
Jay Bazuzi

配列のインデックスは、実際にはインデックスではありません。これは単に、配列の開始点からの距離であるオフセットです。最初の要素は配列の先頭にあるため、距離はありません。したがって、オフセットは0です。

22
Thomas

その理由は歴史的なものだけではありません。CとC++は今でも広く使用されており、ポインタ演算は配列をインデックス0から開始する非常に有効な理由です。

ポインター演算が不足している他の言語の場合、最初の要素がインデックス0または1のどちらにあるかは、何よりも慣例に近いものです。
問題は、最初の要素としてインデックス1を使用する言語は、孤立して存在せず、通常、多くの場合に記述されているライブラリと対話する必要があるということです-ご想像どおり-CまたはC++ ...

VBとその派生フレーバーは、配列を0または1のいずれかで開始させることに悩まされており、長い間問題の原因となってきました。

結論としては、最初の要素のインデックスが全体を通して一貫している限り、言語が最初の要素のインデックスをどのように考慮してもかまいません。問題は、1を最初のインデックスと見なすと、実際の作業が難しくなることです。

17
Renaud Bompuis

ゼロベースの配列は、Cやアセンブラーにもルーツがあります。 Cでは、ポインター演算は基本的に次のように機能します。

  • 配列の各要素は、特定のバイト数を占めます。 32ビット整数は(明らかに)4バイトです。
  • 配列のアドレスは、配列の最初の要素で占められ、その後に同じサイズの連続したブロック内の後続の要素が続きます。

説明のために、int a[4]は0xFF00にあり、アドレスは次のとおりです。

  • a [0]-> 0xFF00;
  • a [1]-> 0xFF04;
  • a [2]-> 0xFF08;
  • a [3]-> 0xFF0C。

したがって、ゼロベースのインデックスを使用すると、addresの計算は簡単です。

要素のアドレス=配列のアドレス+インデックス* sizeof(type)

実際、Cの式はすべて同等です。

  • a [2];
  • 2 [a];そして
  • *(a + 2)。

1ベースの配列では、計算は(それでも)少し複雑になります。

その理由は主に歴史的なものです。

10
cletus

ゼロベースの配列を使用する場合、配列の長さは有効なインデックスのセットです。少なくとも、それはペアノ​​算術が言うことです:

0 = {}
1 = 0 U {0} = {0}
2 = 1 U {1} = {0,1}
3 = 2 U {2} = {0,1,2}
...
n = n-1 U {n-1} = {0,1,2...n-1}

つまり、ある意味で、これは最も自然な表記法です。

8
pyon

Cでは配列とポインタの間に強い相関があるため

char* p = "hello";
char q[] = "hello";

assert(p[1] == q[1]);

assert(*p == *q)

* pは*(p + 0)と同じです

開始インデックスが1の場合、後で頭痛がします

7
AndersK

ヒープは、1ベースのアレイの利点の一例です。インデックスiが与えられた場合、iの親と左の子のインデックスは

PARENT [i] = i÷2

LCHILD [i] = i×2

ただし、1ベースのアレイのみ。 0ベースの配列の場合

PARENT [i] =(i + 1)÷2-1

LCHILD [i] =(i + 1)×2-1

そして、あなたはiがそのインデックスへのサブ配列のサイズでもあるというプロパティを持っています(つまり、範囲[1、i]のインデックス)。

しかし、結局それは問題ではありません。なぜなら、通常よりも1つ多い要素を割り当て、ゼロを無視することで、0ベースの配列を1ベースの配列にすることができるからです。したがって、適切な場合に1ベースの配列の利点を活用するようにオプトインし、他のほとんどすべての状況でよりクリーンな計算のために0ベースの配列を維持できます。

5
sgm

1ではなく0から始まる理由は、オフセットを配列のメモリの先頭からこの要素までの距離と考えることができるためです。それは私に0番目の要素を与えると言っているのではありません-つまり、最初から0要素である要素を私に与えています。

それを見る別の方法は、これらが(ほとんどの場合)同等であるということです。

array[n]

*(array + n)

標準が変更されないのは、Cが約40年前から存在しているためです。それを変更する説得力のある理由はなく、変更した場合、配列の先頭が0であることに依存する既存のコードはすべて破損します。

4
Dennis Munsie

私の考えでは、それは完全に恣意的だと​​いうことです。ゼロベースまたは1ベースの配列について特別なことは何もありません。 Visual Basicから解放されて以来(ほとんどの場合、Excelで小さなことを行うことがある)、1を基にした配列を扱ったことはありません...同じです。事実配列の3番目の要素が必要な場合、3または2と呼ばれるのは単なる実装の詳細ですただし、配列で行う作業の99%は2つの絶対点にのみ関心があります:最初の要素とカウントまたは長さ。繰り返しますが、最初の要素が1ではなく0と呼ばれる、または最後の要素がcount-1または代わりにcountと呼ばれるのは、単なる実装の詳細です。

編集:一部の回答者は、1ベースの配列はフェンスポストエラーが発生しやすいと述べています。私の経験では、今考えてみるとそうです。 VBで、「これはうまくいくか、1つ離れているため爆破する」と思ったのを覚えています。 Javaそれは決して起こりません。私は良くなっていると思いましたが、一部の回答者は0ベースの配列がより良い算術をもたらすケースを指摘します、あなたが対処する必要がないときでも下位レベルの言語で。

4
Dan Rosenstark

いくつかの元の位置/相対位置情報を含むコードは、0から始まる配列の方がずっときれいです。

たとえば、大きなベクトルの定義された位置にあるベクトルをコピーするコードは、配列が1から始まるので面倒です。

function copyAtPos (dest, vect, i):
    for i from 1 -> vect.length do
        dest[pos+i-1] = vect[i]

0から始まる配列の反対:

function copyAtPos (dest, vect, i):
    for i from 0 -> vect.length-1 do
        dest[pos+i] = vect[i]

あなたが大きな畳み込み式を書き始めたら、それは必需品になります。

4
poulejapon

10年以上のC/C++プログラマーとして、PascalとDelphiの非常に強力なバックグラウンドを持つ私はstill Pascalの強力な配列のバインドとインデックスタイプのチェック、およびそれに伴う柔軟性と安全性がありません。この明白な例は、各月の値を保持する配列データです。

パスカル:

 Type Month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);

  Var Days[Month] of integer;

  ... 
  if Year mod 4 = 0 then // yes this is vastly simplified for leap years and yes i don't know what the comment marker is in Pascal and no i won't go look it up
    Days[Feb] := 29
  else
    Days[Feb] := 28;

+/- 1や「マジックナンバー」を使用せずにC言語で同様のコードを書くことは、かなり困難です。 Days [2]やDays [Jan + Dec]のような式は単にコンパイルされないことに注意してください。これは、まだCやアセンブラーで考える人にとっては残忍に見える可能性があります。

Pascal/Delphi言語には、見逃すことのできない多くの側面があると言わざるを得ませんが、Cのゼロベースの配列は、比較すると「ばかげている」ように見えます。

4
Roddy

なぜアレイを1から始めたいのですか?

_a[x][y]_と言うと、コンパイラはこれをa+(x*num_cols+y)に変換します。配列が1から始まった場合、これはa+(x*num_cols+y-1)になります。これは追加の算術演算になるでしょう毎回配列要素にアクセスしたい。なぜプログラムを遅くしたいのですか?

3
Borealid

なぜ2または3または20ではないのですか?それは、1ベースの配列を持っているのが、ゼロベースの配列を理解するのが何となく簡単または単純であるようなものではありません。 1ベースの配列に切り替えるには、すべてのプログラマーが配列の操作方法を再学習する必要があります。

さらに、既存の配列へのオフセットを処理する場合も、より理にかなっています。配列から115バイトを読み取った場合、次のチャンクは115から始まることがわかります。以下同様に、次のバイトは常に読み取ったバイトのサイズになります。 1ベースの場合は、常に1を追加する必要があります。

また、「真の」ポインタ演算のない言語でも、配列内のデータのチャンクを処理する必要がある場合があります。 Javaでは、メモリマップファイルまたはバッファにデータを含めることができます。その場合、ブロックiのサイズは* iです。1ベースのインデックスでは、ブロック* iになります。 +1。

1ベースのインデックス付けでは、多くの手法で、場所全体で+1が必要になります。

3
Chad Okere

1ベースの配列を使用して、1次元配列を多次元配列に変換します。

int w = 5, h = 5, d = 5;

int[] a1 = new int[w * h * d], new a2 = int[w,h,d];

for (int z = 1; z <= d; z++)

  for (int y = 1; y <= h; y++)

    for (int x = 1; x <= w; x++)

      a1[x + (y - 1) * w + (z - 1) * h] = a2[x,y,z];

配列が1ベースの場合でも、yインデックスとzインデックスは0ベース(y-1、z-1)であることに注意してください。状況によっては、0ベースのインデックスを回避できない場合があります。一貫性を保つために、常に0ベースのインデックスを使用しないのはなぜですか?

3
enjerth

それは、配列がどのように構成されるかによるものです。彼らが1から始めるのはあまり意味がありません。配列は、メモリのベースアドレス、サイズ、およびインデックスです。 n番目の要素にアクセスするには、次のようにします。

base + n * element_size

したがって、0は明らかに最初のオフセットです。

2
Gian

ここで一歩踏み出して、整数の「キー付き」配列とは異なるものを提案します。

あなたの同僚は、私たちが常に1から数える物理的な世界で「セット」の1対1のマッピングを作成していると思います。私はこれを理解することができます。ソフトウェアと物理的な世界の間で1対1にマッピングされている場合。

私の提案

保存するものには整数ベースの配列を使用せず、他の種類の辞書またはキーと値のペアを使用してください。これらは、任意の整数に束縛されないため、実際の生活によく対応します。これは適切な場所であり、ソフトウェアと物理的な世界との間の概念1から1へのマッピングの利点のため、できる限り使用することをお勧めします。

つまり、kvp['Name Server'] = "ns1.example.com";(これは100万の例のうちの1つにすぎません)。

ディスカイマー

これは、数学がコンピュータの実際の実装に近いため、数学に基づいた概念で作業している場合は、まったく機能しません。 kvpセットを使用しても、ここでは何の助けにもなりませんが、実際には混乱し、問題がさらに大きくなります。私は、kvpまたは配列として何かがよりよく機能する可能性があるすべてのコーナーケースを検討していません。

最終的なアイデアは、意味のあるゼロベースの配列またはキーと値のペアを使用することです。ハンマーしかない場合、すべての問題が釘のように見え始めることを覚えておいてください...

2
Bryan Rehbein

個人的には、1つの引数は、配列のインデックスをオフセットと見なす場合です。それは理にかなっています。

その最初の要素と言うこともできますが、配列の起点に対する最初の要素のオフセットはゼロです。そのため、配列Originにゼロを追加すると、最初の要素が生成されます。

したがって、計算では、1を追加してから1を削除するよりも、0を追加して最初の要素を見つける方が簡単です。

低いレベルのことをした人はいつもベースゼロを考えていると思います。そして、多くの場合、非アルゴリズムプログラミングを始めているか、より高いレベルに慣れている人々は、基本的な1つのシステムを望んでいるでしょう。あるいは、過去の経験に偏っているだけかもしれません。

2
Loki

1ベースのインデックスの代わりに0ベースのインデックスを使用した唯一の2つの(非常に)重大な理由は多くのプログラマーの再教育を避けるため AND 後方互換性のためのようです。

あなたが受け取ったすべての回答で、1ベースのインデックスに対する他の深刻な議論を見たことがありませんでした。

実際、インデックスは当然1から始まります、そしてここに理由があります。

最初に、私たちは尋ねなければなりません:配列はから来ましたか?実際の同等物はありますか?答えは「はい」です。コンピュータサイエンスにおいて、これらはベクトルと行列をモデル化する方法です。ただし、ベクトルと行列は、コンピュータ時代の前に1ベースのインデックスを使用していた数学の概念です(現在でも、ほとんどが1ベースのインデックスを使用しています)。

現実の世界では、インデックスは1から始まります。

トーマスが上で述べたように、0ベースのインデックスを使用した言語は、実際には、インデックスではなくoffsetsを使用しています。そして、これらの言語を使用している開発者は、インデックスではなくoffsetsについて考えます。明確に述べられていれば、これは問題にはなりませんが、そうではありません。オフセットを使用する多くの開発者はまだインデックスについて話します。また、インデックスを使用する多くの開発者は、C、C++、C#などがオフセットを使用することをまだ認識していません。

言葉遣いの問題です。

(Diskstraの論文についての注記-上記で私が言ったことを正確に述べています:数学者1ベースのインデックスを使用してください。しかし、Diskstraはその仲間を考えるすべきではない =一部の式が醜くなるので、それらを使用します(例:1 <= n <= 0)。まあ、彼がその式で正しいかどうかはわかりません-これらの例外的な空のシーケンスを回避するために、このようなパラダイムシフトを行うことは非常に多いようです少しの結果のためのトラブルの...)

2
Sylvain

「20世紀」に実際に1900年代を言及することに悩まされたことはありますか?まあ、これは、1ベースの配列を使用する場合に常に処理する面倒なことの良いアナロジーです。

.net IO.stream読み取りメソッドのような一般的な配列タスクを考えてみましょう:

int Read(byte[] buffer, int offset, int length)

以下は、0ベースの配列の方が優れていると確信させるために行うことをお勧めします。

各インデックススタイルで、読み取りをサポートする BufferedStream クラスを記述します。 1ベースの配列の読み取り関数の定義を変更できます(オフセットの代わりに下限を使用するなど)。特別なことは何も必要ありません。単純にしてください。

さて、それらの実装のどれがより簡単ですか?どちらに+1と-1のオフセットが散在していますか?私もそう思っていました。実際、インデックスのスタイルが問題にならない唯一のケースは、Setのように配列ではないものを使用する必要がある場合だと主張します。

2
Craig Gidney

「自分の」コードを書くときに気をつければ可能です。インデックスは、すべてのn> = 0に対してnから始まると想定し、それに応じてプログラムできます。

標準に関して、Borealidは大きな議論を持っています。

1
Praveen S

Cの配列は、ポインター演算の省略形です。このケースを考えてみましょう:

struct Foo *foo;
struct Foo foos[10];

foo = &(foos[1]);
foo = foos + (1 * sizeof(struct Foo));

最後の2行は同じことを意味します。初期オフセットを変更すると、この相関関係が壊れ、Cでの多くのことがはるかに困難になります。

Pascalのような非常に厳密な配列型を持ついくつかの言語では、他の方法でカウントを開始できます。しかし、CおよびCから派生した多くの言語では、配列はポインター演算の省略形にすぎないため、開始インデックスをいじることはできません。

1
Rob Napier

なぜなら、配列名は配列の開始位置への定数ポインターだからです。たとえば、Cでは、array [2]はarray +(sizeof(array)* 2)に変換されます。これにより、開始要素(3番目の要素:))を超える2つの要素が得られます。したがって、同じ計算で開始要素に到達したい場合は、

配列+(sizeof(配列)* i)=配列

(sizeof(array)* i)= 0

i = 0

簡単な方程式の数学。

1
LostMohican

これを実装するには、実際にはいくつかの方法があります。

  • 0ベースの配列インデックス
  • 1ベースの配列インデックス
  • 0または1ベースの配列(VB 6.0 ...のように...これは本当に恐ろしいです)

最終的に、言語が0または1ベースの配列を使用するかどうかは重要ではないと思います。しかし、ほとんどのプログラマーが慣例に慣れているという単純な理由で、0ベースの配列を使用するのが最良の選択だと思います。これは、すでに記述されたコードの大部分と一致しています。

ただし、実際に失敗する可能性がある唯一の方法は、Visual Basicのように一貫性を欠くことです。私が現在維持しているコードベースは、0と1ベースの配列に分割されています。どちらがどれかを理解するのは非常に困難です。これは、迷惑なほど冗長なforループにつながります。

dim i as integer, lb as integer, ub as integer
lb = LBound(array)
ub = UBound(array)
for i = lb to ub
       '...
next
1
Colin

それはまさにそうであり、何年も前から存在しています。それを変更すること、またはそれを議論することは、変化する信号機を変更または議論することと同じくらい無意味です。 blue = stop、red = goにしましょう。

C++の数値レシピで時間をかけて行われた変更を調べてください。彼らはマクロを使用して1ベースのインデックスを偽造していましたが、2001年版ではあきらめて群れに加わりました。彼らのサイトwww.nr.comでこれの背後にある理由について啓発的な資料があるかもしれません

ところで、配列から範囲を指定するバリアントもあります。例:python vs. IDL; a [100:200] vs a [100:199]は100要素を取得します。各言語の癖を学ぶ必要があります。それを行う言語を変更するにはもう一方を一致させる方法の1つは、そのような歯のカッシングと歯ぎしりを引き起こし、実際の問題を解決しません。

1
DarenW

線形コレクション内のアイテムのlocationについて話すとき、ゼロは自然です。

本でいっぱいの棚を考えてください-最初の本は棚の側壁と同じ高さにあります-それは場所ゼロです。

したがって、配列インデックスを、物を見つける手段または物を参照する手段のどちらにするかによって異なると思います。

1
f100

モジュロ(およびモジュロに使用する場合はAND演算子)は常にいくつかの値に対して0を返すため、私は0ベースのインデックスを好みます。

私はしばしば次のような配列を使用しています。

int blah = array[i & 0xff];

1ベースのインデックスを使用すると、この種のコードがよく間違えます。

1
Laserallan

文字列検索やさまざまな並べ替え/マージアルゴリズムなどの配列ベースのコードを多数プログラミングしたり、1次元配列で多次元配列をシミュレートしたりせずに0ベースを防御することは困難です。 Fortranは1ベースであり、この種のコードを正しく実行するには多くのコーヒーが必要です。

しかし、それはそれをはるかに超えています。要素のインデックスではなく、何かの長さを考えることができることは、非常に役立つ精神的習慣です。たとえば、ピクセルベースのグラフィックを実行する場合、座標をピクセル上ではなくピクセル間にあると考える方がはるかに明確です。これにより、3x3の長方形には16ではなく9ピクセルが含まれます。

もう少し遠い例は、解析、またはテーブルの小計の印刷を先読みするアイデアです。 「常識的な」アプローチでは、1)次の文字、トークン、またはテーブル行を取得し、2)それをどう処理するかを決定します。先読みアプローチでは、1)表示できると想定して、必要かどうかを決定します。2)必要な場合は、「受け入れ」ます(次の表示が可能になります)。次に、擬似コードを書き出すと、はるかに簡単になります。

さらに別の例は、MS-DOSバッチファイルなど、選択のない言語で「goto」を使用する方法です。 「常識的な」アプローチは、実行するコードのブロックにラベルを付け、そのようにラベルを付けることです。多くの場合、より優れたアプローチは、コードブロックをスキップするために、コードブロックの最後にラベルを付けることです。これにより、「構造化」され、変更がはるかに容易になります。

1
Mike Dunlavey

他の人が述べたように、それは数学をより簡単にするので、私は0ベースの配列を好みます。たとえば、10x10グリッドをエミュレートする1​​00要素の1次元配列がある場合、行r、列cの要素の配列インデックスiは次のようになります。

 0ベース:i = 10 * r + c 
 1ベース:i = 10 *(r-1)+ c 

そして、インデックスiを指定すると、行と列に戻ると次のようになります。

 0ベース:c = i%10 
 r = floor(i/10)
 1ベース:c =(i-1)%10 + 1 
 r = ceil(i/10)

1ベースの配列を使用すると、上記の計算が明らかに複雑になるため、0ベースの配列を標準として選択するのは理にかなっているようです。

ただし、2Dデータを1D配列で表現する理由があると思うので、誰かが私のロジックに欠陥があると主張するかもしれません。私はC/C++でこのような状況に遭遇しましたが、そのような計算を実行する必要があることは言語に依存することを認めなければなりません。配列が常にクライアントのすべてのインデックス計算を実行した場合、コンパイラーはMベースの配列アクセスをコンパイル時に0ベースに変換し、これらの実装の詳細をすべてユーザーから隠すことができます。実際、どのようなコンパイル時定数も同じ操作セットを実行するために使用できますが、そのような構成はおそらく理解できないコードにつながるだけです。

おそらく、より良い議論は、1ベースの配列を使用する言語で配列のインデックス操作の数を最小化するには、天井関数を使用して整数除算を実行する必要があるということです。ただし、数学的な観点から、整数除算はd剰余rを返す必要があります。ここで、dとrは両方とも正です。したがって、0ベースの配列を使用して数学を簡略化する必要があります。

たとえば、N個の要素を持つルックアップテーブルを生成する場合、値xの配列への現在の値の前の最も近いインデックスは次のようになります(概して、丸め前の結果が整数である値は無視されます)。

 0ベースの床:floor((N-1)* x/xRange)
 1ベースの床:floor((N-1)* x/xRange)+ 1 
 1ベースのceil:ceil((N-1)* x/xRange)

切り捨ての標準的な規則が使用されている場合、1ベースの配列は追加の演算を必要とすることに注意してください。これは望ましくありません。この種の数学は、舞台裏で何が起こっているかについての低レベルの知識を必要とするため、コンパイラによって非表示にすることはできません。

1
Jeff G

私は、プログラマーが日々の考え方で0ベースの配列の直観に反することにうんざりしていて、配列を説明するより直感的な方法を主張していたに違いない。皮肉なことに、人間が「クラス」を作成するために多くの時間を費やして、コードでより人間的な方法で物事を説明できるようになりましたが、0対1の配列を見ると、ハングアップしているようです。それだけの論理。

コンピュータに関する限り、数学的には0がおそらくより良いでしょうが、ここでポイントが見落とされているように感じます。もっと人間的な方法(クラスなど)で説明したい場合は、言語の他の部分でも同じようにしたくないのですか?言語を人間にとってより理解しやすく、使いやすくするために、論理的にも有効でもない(または優先度を高くする)ことはありませんか?つまり、ロジックバグが発生しやすく、傾向が強いシナリオになりにくくなります。使用可能な創造物のより速い生産に。 PHP例:

array(1 => 'January', 'February', 'March');

リクエストごとに1ベースの配列を提供します。

なぜnotが規範を持っているのですか?

array('January', 'February', 'March');

そして例外は:

array(0 => 'Value for scenario where 0 *has* to be used as the key',
      'value2', 'value3');

例えばPHPの場合、私の賭けは80%ですが、1ベースの配列がデフォルトの構文であるので、実際のユースケースでの論理バグを減らすか、少なくとも平均してそれ以上は起こさず、多くのことをしますコーダーでより簡単に使用可能なコードをより速く生成します。必要なときのために、array(0 => 'value')のオプションがまだあると思いますが、ほとんどの場合、現実世界の説明に近いものが実用的であると想定しています。

この観点から見ると、これは実際に要求のフェッチが長すぎるとは思えません。インターフェースに近づくとき、それがOSであろうと、プログラマー向けの言語であろうと、人間の思考や習慣に近づけば近づくほど、多くの場合、私たちは幸福になり、人間とコンピュータの間の誤解が少なくなります(人間の論理-バグ)、およびより速い生産などがあります。現実の世界で80%の場合、リストを作成したり数えたりするときに1で物事を説明する場合、コンピューターは理想的には、私の情報を最小限の情報で理解できるように解釈するか、または何かを説明する通常の方法から変更する必要があります。要するに、現実世界をモデル化できるほど、抽象化の品質が向上します。つまり、それが究極の目標であり、抽象化の必要性の証拠となるため、彼が望んでいるのは決して愚かなことではありません。コンピュータは、最終的にはそれを0ベースの配列の特別な使用と見なすことができます。時間が経つにつれバグが少なくなり、自分が何をしたいのかを説明するためのより簡単で直感的な方法である限り、コンピューターがそれを解釈する方法を気にする必要はありません。

だから、それは私の2セントです。彼が何を言っているのか、または何が解釈されたのか、彼が何を意味していたのかを真剣に疑っています。彼がおそらく意味したことは、「自分が欲しいものをコンピュータに伝える直感的ではない方法を持つのが嫌いだ」ということでした。 :)私たちは皆ではありませんか?笑。

1
Brian Chandler

アレイを1から始めると主張した場合...

type real_foo[COUNT], *const foo=real_foo-1;

あなたが本当にサディスティックであるなら、あなたはこれをするためにプリプロセッサマクロを作ることさえできます...

#define CONCAT(x,y) x ## y
#define ARRAY1(name,size) CONCAT(real_, name), *const name=CONCAT(real_, name)-1
type ARRAY1(foo, COUNT);

それらのマクロを台無しにしていないことを願っています...

Cは非常に単純な言語です(またはそう言っています)。その設計は、利用可能なハードウェア機能、つまりコンパイラーの実装のしやすさに強く基づいていました。配列のインデックスを作成すると、直接ポインタ演算に変換されるため、次のようになります。

int array[256];
int i = 10;
...
array[i] = 12;

次のようなものに変換されます:

*(array + i*sizeof(int)) = 12;

または架空の中間機械語で:

load value of array into register Ra
load value of i into register Rb
right shift Rb by X number of bits
add value in Rb to value in Ra
store value 12 at address in Ra

(注:ここでarrayはロード時に固定されたアドレスなので、「arrayのロード値」と言います-ポインタのインデックス作成には少し異なります。取得するには、もう1つの間接参照が必要ですベースアドレス)

これを念頭に置いて、ゼロベースのインデックス付けは自然なことです。

0

確かに可能ですが、C言語はこれをサポートしていません。 Fortran、PL/1、Pascal、Modula2、Adaなどの他の多くの言語がこれをサポートしています。

これは、コンパイラをシンプルかつ小型に保つためのC言語設計の一部であり、あまりにも多くのものを壊して変更することはできません。

0
progrmr

できますが、コンパイラの最適化は無効な結果を作成することは自由です(gccとmsvcはできませんが、clangはできます)。

char* myArray = malloc(100) - 1;
/* now myArray[1] is the first element, and myArray[100] is the last. */
free(myArray + 1);

ただし、他の人が述べたように、これを行わないでください。 1から始めるという悪い習慣の学習をやめます。

別の解決策:配列の最初の要素を無視するだけです。

0
Clark Gaebel

私は、コードがより低いレベルでどのように機能するかを考えようとしているときに、これが必要になった場合、ゼロベースの配列がこのより低いレベルにより適切にマッピングされると思います。また、ポインタを正しく機能させるには、1ベースの配列のすべての逆参照に対して追加のadd命令が必要になると思います。私はこれを回避する方法の1つを見ています(ポインターを0th/1stではなく、配列の-1st/0th要素にポイントします)。

0
dsimcha

最もアトミックなレベルでは、それは単にバイナリであることに起因します。

0   =>  0 
1   =>  1
10  =>  2
11  =>  3

「0」は最初の2進数であり、同様に配列は「1番目」の2進数でインデックスが付けられます。

また、データセットの任意のベースアドレスを指定すると、最初のレコードはそのアドレスに存在し、そのアドレス+ 1ではありません

$base = 010110; 
$firstvalue  = $base + 0 * $unitsize
$secondvalue = $base + 1 * $unitsize
$thirdvalue  = $base + 2 * $unitsize

「1」ベースの配列がある場合、内部システムは、データが格納されている基になるメモリアドレスを見つけるために、常にターゲット値を1ずつ減らさなければなりません。

0は、最初の10進数と同じくらい最初の2進数です。説得力がない。

ページに数値「0」を書き込みます。番号はいくつありますか?

ページに500〜550の数字がある場合、いくつの数字がありますか?私は51人います!ただし、differenceはわずか50です。

50頭の羊をパドックに入れます。驚くべきことに、それらはどれも数字のようには見えませんが、それでも数えられる量です。

しかし、これはcrazyという考えです。羊が0に少し似ている場合は、使用しないでください。代わりに50匹の羊を見つけなければなりません。

簡略化

カウントには1〜Nを使用します。ただし、0は依然としてシンボルを保持する有効な場所であるため、使用する必要があります。

0がシンボルを保持する有効な場所でない場合は、10、20、30、40などを番号システムからスクラップし、9から11にまっすぐ進む必要があります。

1 2 3 4 5 6 7 8 9 11 12 13 14 15 16 17 18 19 21 22 23 24 25 26 27

本質的に、「0」で始まらない人間は[〜#〜] cultural [〜#〜]のことです。それは合理的なものではありません。

0
Kent Fredric

ゼロベースの配列では、unsigned intをインデックスとして使用でき、下限の範囲外のインデックスをテストする必要はありません。例えば:

int GetValue(unsigned index)
{
    ASSERT(index < arraySize);
    return(array[index];
}
0
Jason Williams