Warum sind nullbasierte Arrays die Norm?


112

Eine hier gestellte Frage erinnerte mich an eine Diskussion mit einem anderen Programmierer. Er argumentierte, dass nullbasierte Arrays durch einsbasierte Arrays ersetzt werden sollten, da nullbasierte Arrays ein Implementierungsdetail sind, das von der Art und Weise herrührt, wie Arrays und Zeiger sowie Computerhardware funktionieren Sprachen.

Jetzt kann ich nicht wirklich gut debattieren, daher kann ich keine guten Gründe nennen, bei nullbasierten Arrays zu bleiben, die sich nicht angemessener anfühlen. Warum ist Null der gemeinsame Ausgangspunkt für Arrays?


In einem Array mit n Elementen ist das Element 'n' nicht vorhanden. Ein Array mit n Elementen hat nur Mitglieder mit einer Zahl von 0 bis n-1. Ist es nicht besser, wenn wir ein Array haben, das mit 1 beginnt, und so repräsentiert ein Array mit n Elementen tatsächlich die n Elemente, die im Array vorhanden sind.

Ich mag nullbasierte Arrays, weil das erste Bit in einem Byte 2 ^ 0 ist, nicht 2 ^ 1. Das hilft mir manchmal :)
E-MEE

Wenn man sich diese Liste zum Beispiel en.wikipedia.org/wiki/… ansieht , wird man feststellen, dass die meisten domänenspezifischen Sprachen bei 1 indiziert werden und die meisten Sprachen der cs school of thinking bei 0. Wenn man etwas länger sucht Er wird bemerken, dass zu diesem Thema so viele dumme Diskussionen geführt wurden, dass es wahrscheinlich sinnlos ist, zu versuchen, einen der beiden zu sprechen, um ihre Art zu ändern. Bei der Verteidigung von "1" verwenden die meisten Menschen auf der Welt diese. Die meisten Programmierer verwenden "0". Die meisten Leute verstehen Programmierer nicht, so dass Sie denken ...
Rook

Ich kann nicht glauben, dass diese Frage von StackOverflow auf Programmierer migriert und dann als nicht konstruktiv geschlossen wurde. Das ist doof.
Paercebal

Antworten:


105

Ich denke, keiner von uns kann ein stärkeres Argument liefern als der Artikel von Edsger W. Dijkstra "Warum die Nummerierung bei Null beginnen sollte" .


Er hat ein paar Statistiken aufgerufen und einen mathematischen Beweis verwendet. Aber ich bin mir sicher, dass sich noch jemand streiten könnte. Obwohl ich das gutheiße, würde ich es nicht tun.

6
In Dijkstras Artikel geht es um Stil, aber in seinen Argumenten geht es um Einfachheit und Benutzerfreundlichkeit ... +1.
Paercebal

80

Berechtigungsargument

Nun ... Anscheinend basieren die meisten Sprachen, einschließlich der jüngsten, auf Null. Da diese Sprachen von sehr erfahrenen Leuten geschrieben wurden, muss sich Ihr Freund geirrt haben ...

Warum eins?

Warum wäre 1 ein besserer Startindex als Null? Warum nicht 2 oder 10? Die Antwort selbst ist interessant, weil sie viel über den Durchdringungsprozess der Leute zeigt, die die Idee verteidigen.

Das erste Argument ist , dass es natürlich ist, weil der erste in der Regel das ist eine vor allen anderen, zumindest für die Mehrheit der Menschen ...

Das Zahlen- ein Argument ist , dass der letzte Index ist auch die Größe des Arrays ...

Ich bin immer noch beeindruckt von der "Qualität" der Gründe, die ich normalerweise für diese Art von Argumenten höre ... und noch mehr, wenn ich daran erinnert werde, dass ...

Warum nicht Null?

... "Eins-basierte" Notationen sind Überbleibsel der westlichen Kultur, die die Existenz von Null jahrhundertelang ignorierten, wenn nicht sogar mehr.

Ob Sie es glauben oder nicht, der ursprüngliche gregorianische Kalender geht von -3, -2, -1, 1, 2, 3 ... Versuchen Sie sich das Problem vorzustellen, das es für die westliche Wissenschaft mit sich brachte (zum Beispiel wie viele Jahre vom 1. Januar bis zum 2. Januar) bis zum 1. Januar 2, um zu sehen, dass der ursprüngliche gregorianische Kalender mit etwas so Einfachem wie Subtraktion in Konflikt steht ...).

Das Festhalten an einbasierten Arrays ist wie (nun, ich werde dafür herabgestuft ... ^ _ ^ ...), das Festhalten an Meilen und Yards im 21. Jahrhundert ...

Warum Null? Weil es Mathe ist!

Zuerst (Ups ... Entschuldigung ... ich werde es noch einmal versuchen)

Null , Null ist nichts, man ist etwas. In einigen religiösen Texten heißt es: "Am Anfang war nichts". Einige Computerdiskussionen können so brennend sein wie religiöse Debatten, daher ist dieser Punkt nicht so thematisch, wie es scheint ... ^ _ ^

Erstens ist es einfacher, mit einem nullbasierten Array zu arbeiten und dessen nullten Wert zu ignorieren, als mit einem einbasierten Array zu arbeiten und den nullten Wert zu finden. Dieser Grund war fast so dumm wie der vorherige, aber das ursprüngliche Argument für einseitige Arrays war auch ein ziemlicher Trugschluss.

Zweitens : Wenn Sie mit Zahlen arbeiten, stehen die Chancen gut, dass Sie sich mit Mathematik befassen, und wenn Sie sich mit Mathematik befassen, stehen die Chancen gut, dass Sie nicht in der Stimmung sind, veraltete Konventionen zu umgehen. Die einseitige Notation plagte Mathe und Datumsangaben über Jahrhunderte hinweg, und indem wir aus unseren Fehlern lernen, sollten wir uns bemühen, dies in zukunftsorientierten Wissenschaften (einschließlich Computersprachen) zu vermeiden.

Drittens : Weisen Sie Computersprachen-Arrays, die an Hardware gebunden sind, ein C-Array mit 21 Ganzzahlen zu, und bewegen Sie den Zeiger 10 nach rechts, um ein natürliches [-10 bis 10] -Array zu erhalten. Dies ist für Hardware nicht selbstverständlich. Aber es ist für Mathe. Natürlich könnte Mathematik überholt sein, aber als ich das letzte Mal nachgesehen habe, glaubten die meisten Menschen auf der Welt, dass dies nicht der Fall ist.

Viertens : Wie bereits an anderer Stelle erwähnt, wäre der erste Index auch für diskrete Positionen (oder auf diskrete Werte reduzierte Entfernungen) Null, wie der Fußboden in einem Gebäude (beginnend bei Null), der abnehmende Countdown (3, 2, 1, NULL) !), die Bodenhöhe, das erste Pixel eines Bildes, die Temperatur (null Kelvin, für den absoluten Nullpunkt oder null Grad Celsius, als Wassergefriertemperatur von 273 K). In der Tat ist das einzige, was wirklich mit einem beginnt, die traditionelle Art des " Ersten , Zweiten , Dritten usw." Iterationsnotation , die mich natürlich zum nächsten Punkt führt ...

Fünf der nächste Punkt (die natürlicherweise das folgende vorherigen ) ist , dass High-Level - Container zugegriffen werden soll, nicht durch den Index, sondern von Iteratoren , es sei denn , die Indizes selbst einen intrinsischen Wert haben. Ich bin überrascht, dass Ihr Verfechter der "höheren Sprache" das nicht erwähnt hat. In dem Fall, dass der Index selbst wichtig ist, können Sie wetten, dass Sie die Hälfte der Zeit eine mathematische Frage haben. Sie möchten also, dass Ihr Container mathematikfreundlich und nicht mathematisch deaktiviert ist, wie z. B. "Ihr alter Gregorianischer Kalender", der bei 1 beginnt, und dass er wiederauferlegte Hacks benötigt, damit er funktioniert.

Fazit

Das Argument Ihres Programmierkollegen ist ein Irrtum, weil es die gesprochenen / geschriebenen Sprachgewohnheiten, die von Natur aus verschwommen sind, unnötig mit Computersprachen verknüpft (bei denen Sie nicht möchten, dass Ihre Anweisungen verwischt werden) und weil eine Hardware falsch zugeordnet wird Aus diesem Grund hofft er, Sie zu überzeugen, dass das nullbasierte Array der Vergangenheit angehört, da die Sprachen in der Abstraktion immer höher werden.

Nullbasierte Arrays sind aus mathematischen Gründen nullbasiert. Nicht aus Hardware-Gründen.

Wenn dies ein Problem für Ihren Programmierkollegen ist, lassen Sie ihn mit echten Konstrukten auf hoher Ebene wie Iteratoren und foreach-Schleifen programmieren.


Grad Null ist 273,15K;)

2
Ich weiß (ich habe ein Master-Diplom in Physik), aber ich hatte das Gefühl, mit Dezimalstellen zu spielen, war weniger der Punkt als die humorvolle Seite, die ich versuchte, meine Argumente mit ... ^ _ ^ ...
paercebal 31.12.08

20
Ihre Absätze tragen die Bezeichnung "Null, Erstens, Zweitens, Drittens, Vier, Fünf". Aus Gründen der Konsistenz sollten Sie entweder Kardinalzahlen ("Null, Eins, Zwei, Drei, Vier, Fünf") oder Ordinalzahlen ("Null, Erste, Zweite, Dritte, Vierte, Fünfte") verwenden. :-)
ShreevatsaR

10
In ähnlicher Weise sind wir für das erste Jahr unseres Lebens nicht ein Jahr alt, sondern null Jahre alt

3
@ Nikita Rybak: Was erstaunlich ist, ist, dass Sie das verpasst haben, was alle Kommentatoren vor Ihnen gesehen haben: Natürlich ist die Antwort von Bill the Lizard die richtige. Aus diesem Grund habe ich ihn mit +1 bewertet, und aus diesem Grund wurde es als beste Antwort für die Frage ausgewählt. In meiner Antwort gehe es eher darum, mich über die trügerischen Gründe für 1-basierte Arrays lustig zu machen und konkrete Fälle aufzuzeigen, in denen ein 1-basiertes Array störend wäre. Trotzdem bin ich überrascht, dass Sie "nicht einen einzigen überzeugenden" gefunden haben, auch wenn die Gründe mit Ironie vermischt sind ...
paercebal

47

Halboffene Intervalle komponieren gut. Wenn Sie es 0 <= i < limmit nElementen zu tun haben und diese erweitern möchten , haben die neuen Elemente Indizes im Bereich lim <= i < lim + n. Das Arbeiten mit nullbasierten Arrays erleichtert das Rechnen beim Teilen oder Verketten von Arrays oder beim Zählen von Elementen . Man hofft, dass die einfachere Arithmetik zu weniger Zaunpfostenfehlern führt .


+1 für halboffene Intervalle - es macht einfach alles einfacher.
Eclipse

29

Bestimmte Arten der Array-Manipulation werden bei 1-basierten Arrays verrückt kompliziert, bleiben jedoch bei 0-basierten Arrays einfacher.

An einem Punkt habe ich einige numerische Analyseprogramme erstellt. Ich habe mit Algorithmen gearbeitet, um komprimierte, spärliche Matrizen zu manipulieren, die sowohl in FORTRAN als auch in C ++ geschrieben wurden.

Die FORTRAN-Algorithmen hatten viel a[i + j + k - 2], während die C ++ hatten a[i + j + k], weil das FORTRAN-Array 1-basiert war, während das C ++ - Array 0-basiert war.


Genau. Der einzige Moment, in dem ich ein 1-basiertes Array nützlich finde, ist, wenn ich Platz für einen Null-Item-Index schaffen möchte. Zum Beispiel, wenn ich ein Array von Objekten habe und deren Indizes als Handles verwende und ein Null-Handle haben möchte.
Fabio Ceconello

Ich habe auch die unnötige Komplikation von 1-basierten Arrays gemerkt. 0-basierte Arrays haben nach meiner begrenzten Erfahrung immer klareren Code für die Array-Indizierung erzeugt, mit der seltenen Ausnahme.

Wie würden sich die Indizes FORTRAN und C ++ um 2 unterscheiden, wenn ihre jeweiligen Indizes nur um 1 versetzt wären? Auch warum minus 2? Wenn FORTRAN auf 1 basiert, würden Sie dann nicht 2 (oder 1) hinzufügen?
RexE

@RexE: So funktioniert es und deshalb ist es bei 1-basierten Arrays so kompliziert.
Jay Bazuzi

@RexE: Angenommen, Sie emulieren ein 3D-Array mit einem flachen. In der Basis 0 entspricht das Element (0 0 0) dem Element 0 im flachen Array. OTOH, wenn es 1-basiert ist, entspricht Element (1 1 1) Element 1 im flachen Array: 1 + 1 + 1-2.

22

Der Index in einem Array ist eigentlich kein Index. Es ist einfach ein Versatz, der der Abstand vom Anfang des Arrays ist. Das erste Element befindet sich am Anfang des Arrays, sodass kein Abstand besteht. Daher ist der Offset 0.


3
Für die meisten Sprachen, die heutzutage entworfen werden, ist dies wirklich ein Implementierungsdetail, das nicht in der Sprache erscheinen sollte (außer wenn es andere bessere Gründe dafür gibt)
Jens Schauder

17

Die Gründe sind nicht nur historisch: C und C ++ sind immer noch verbreitet und Zeigerarithmetik ist ein sehr gültiger Grund dafür, dass Arrays bei Index 0 beginnen.

Für andere Sprachen ohne Zeigerarithmetik ist es eher eine Konvention als etwas anderes, ob sich das erste Element am Index 0 oder 1 befindet.
Das Problem ist, dass Sprachen, die Index 1 als erstes Element verwenden, nicht im luftleeren Raum existieren und normalerweise mit Bibliotheken interagieren müssen, die oft in C oder C ++ geschrieben wurden.

VB und seine abgeleiteten Aromen haben darunter gelitten, dass Arrays entweder bei 0 oder 1 beginnen, und dies war lange Zeit eine Quelle von Problemen.

Fazit ist: Es spielt keine Rolle, in welcher Sprache der erste Elementindex angezeigt wird, solange er durchgehend konsistent ist. Das Problem ist, dass es in der Praxis schwieriger ist, mit 1 als erstem Index zu arbeiten.


Einverstanden. Konsistenz ist wichtig, und wenn Sie nicht den Luxus haben, Code auf niedriger Ebene (einschließlich C / C ++) vollständig zu vermeiden, ist die Arbeit mit 1-basierten Arrays nur problematisch.
Shog9

Dabei stellt sich die Frage: Verwenden Sie Low-Level-Code jemals plattformunabhängig? Mit anderen Worten, Sie befinden sich immer auf der einen oder anderen Plattform und müssen wissen, welche, richtig?
Dan Rosenstark

1
Als jemand, der VB .NET für gewöhnlich für unfair hält, muss ich sagen, dass die VB .NET-Praxis für Arrays schrecklich ist. Sie teilen den Unterschied auf und machen es noch verwirrender: Arrays beginnen bei 0, aber Dim a as Integer (5) erzeugt ein Array mit 6 Positionen. Das Grundprinzip schien zu sein, dass es besser war, eine zusätzliche Position einzunehmen, als Fehler zu haben, die über die Länge des Arrays hinausgingen. Leider haben sie sich in dieser (und anderen) Hinsicht (wie Und und Oder) an die Anforderungen vieler VB6-Programmierer gewöhnt, die VB .NET ohnehin nicht nutzten.
Kyralessa

1
@Kyralessa: Nein, das Ziel war die Abwärtskompatibilität zu VB6 (Assistent für automatische Upgrades…), obwohl sie sich bewusst waren, dass die Notation nicht intuitiv und fehleranfällig ist. Auf der anderen Seite, Andund Orbitweise zu sein hat nichts mit VB6 zu tun, ist dies die einzige logische Lösung für eine Sprache vom Typ VB. Sie tun haben AndAlsound OrElsefür Ihre logischen Operationen.
Konrad Rudolph

Andund Orbitweise zu sein hat alles mit VB6 zu tun, weil sie bitweise in VB6 waren. Die hässlichen Operatoren AndAlsound OrElsesollten bitweise erfolgen, da bitweise Operationen weitaus seltener sind als logische. Es gibt viele hässliche Warzen wie diese, die aufgrund der "Abwärtskompatibilität" in der Sprache verbleiben, wie die Tatsache, dass ByVal überall verputzt ist, obwohl es die Standardeinstellung ist.
Kyralessa 30.03.10

10

Nullbasierte Arrays haben ihre Wurzeln in C und sogar in Assembler. Mit C funktioniert die Zeigermathematik im Grunde so:

  • Jedes Element eines Arrays belegt eine bestimmte Anzahl von Bytes. Eine 32-Bit-Ganzzahl ist (offensichtlich) 4 Bytes;
  • Die Adresse eines Arrays wird vom ersten Element des Arrays belegt, gefolgt von nachfolgenden Elementen in gleich großen zusammenhängenden Blöcken.

Zur Veranschaulichung sei angenommen, dass int a[4]bei 0xFF00 die Adressen sind:

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

Mit nullbasierten Indizes ist die Adressberechnung also einfach:

Adresse des Elements = Adresse des Arrays + Index * Größe von (Typ)

Tatsächlich sind die Ausdrücke in C alle gleich:

  • a [2];
  • 2 [a]; und
  • * (a + 2).

Bei einbasierten Arrays ist die Mathematik (noch dazu) etwas komplizierter.

Die Gründe sind also weitgehend historisch.


1
Die ursprüngliche Frage besagt bereits, dass "Arrays, die auf Null basieren, ein Implementierungsdetail sind, das von der Funktionsweise von Arrays, Zeigern und Computerhardware herrührt, aber solche Dinge sollten nicht in höheren Sprachen widergespiegelt werden."

Erwähnenswert ist, dass Sprachen, die N-basierte Arrays zulassen, normalerweise Code mit Array-Offsets generieren, die automatisch zu Laufzeitkosten von null berechnet werden.
Roddy

8

Wenn Sie nullbasierte Arrays verwenden, entspricht die Länge des Arrays der Menge der gültigen Indizes. Zumindest sagt das die Peano-Arithmetik:

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}

In gewissem Sinne ist es die natürlichste Notation.


7

Weil es in C eine starke Korrelation zwischen Arrays und Zeigern gibt

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

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

assert(*p == *q)

* p ist dasselbe wie * (p + 0)

mit einem Anfangsindex von 1 bekommen Sie später Kopfschmerzen


5

Ein Heap ist ein Beispiel für die Vorteile von 1-basierten Arrays. Wenn ein Index i gegeben ist , ist der Index des Elternteils und des linken Kindes von i

PARENT[ i ] = i ≤ 2

LCHILD[ i ] = i × 2

Aber nur für 1-basierte Arrays. Für 0-basierte Arrays haben Sie

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

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

Und dann haben Sie die Eigenschaft, dass i auch die Größe des Subarrays zu diesem Index ist (dh Indizes im Bereich [1, i ]).

Aber am Ende spielt es keine Rolle, denn Sie können ein 0-basiertes Array in ein 1-basiertes Array umwandeln, indem Sie ein Element mehr als normal zuweisen und die Null ignorieren. Auf diese Weise können Sie die Vorteile von 1-basierten Arrays bei Bedarf aktivieren und die 0-basierten Arrays für eine sauberere Arithmetik in fast allen anderen Situationen beibehalten.


4

Mein Gefühl ist, dass es völlig willkürlich ist. An null- oder einbasierten Arrays ist nichts Besonderes. Seit ich mich von Visual Basic befreit habe (meistens mache ich kleine Dinge in Excel), habe ich nicht mehr mit 1-basierten Arrays gearbeitet, und ... es ist dasselbe. Tatsache ist, dass, wenn Sie das dritte Element des Arrays benötigen, es nur ein Implementierungsdetail ist, das als 3 oder 2 bezeichnet wird. 99% Ihrer Arbeit mit Arrays sind jedoch nur an zwei absoluten Punkten interessiert: dem ersten Element und die Zählung oder Länge. Auch hier handelt es sich nur um ein Implementierungsdetail, bei dem das erste Element null statt eins heißt oder das letzte Element count-1 oder stattdessen count heißt.

Bearbeiten: Einige der Antwortenden haben erwähnt, dass 1-basierte Arrays anfälliger für Fencepost-Fehler sind. Nach meiner Erfahrung, wenn ich jetzt darüber nachdenke, ist dies wahr. Ich erinnere mich, dass ich in VB gedacht habe: "Das wird entweder funktionieren oder explodieren, weil ich um eins abwesend bin." In Java passiert das nie. Obwohl ich dachte, dass ich besser werde, weisen einige der Antwortenden auf Fälle hin, in denen 0-basierte Arrays zu einer besseren Arithmetik führen, AUCH, wenn Sie sich nicht mit einer niedrigeren Sprache auseinandersetzen müssen.


In PHP geben die meisten Suchvorgänge in der Zeichenfolgenfunktion FALSE on not found zurück. Nicht -1.
jmucchiello

Sie verwechseln die Zählung mit dem Index des letzten Elements. Die Anzahl der leeren Arrays ist immer 0, unabhängig davon, ob Sie nullbasierte oder einsbasierte Arrays verwenden. Der Vorteil von einbasierten Arrays ist, dass die Anzahl die Position des letzten Elements ist (aber das ist ungefähr der einzige Vorteil).

Wahr in Bezug auf beide dieser Punkte: Letzte Hälfte gelöscht, da dies für nullbasierte oder einsbasierte Arrays gleich ist: count ist 0, wenn Sie null Elemente haben.
Dan Rosenstark

Ich meinte die letzte Hälfte meiner Antwort ...
Dan Rosenstark

4

Als C / C ++ - Programmierer von über 10 Jahren mit sehr guten Kenntnissen in Pascal und Delphi vermisse ich immer noch Pascals starke Überprüfung von Array-Grenzen und Indextypen sowie die damit verbundene Flexibilität und Sicherheit. Ein offensichtliches Beispiel hierfür sind Array-Daten, die Werte für jeden Monat enthalten.

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;

Das Schreiben von ähnlichem Code in C-Sprachen ohne Verwendung von +/- 1 oder 'magischen Zahlen' ist eine ziemliche Herausforderung. Beachten Sie, dass Ausdrücke wie Days [2] und Days [Jan + Dec] einfach nicht kompiliert werden können, was für Leute, die noch in C oder Assembler denken, brutal erscheinen kann.

Ich muss sagen, dass es viele Aspekte von Pascal / Delphi-Sprachen gibt, die mir nicht entgehen, aber Arrays auf C-Null-Basis scheinen im Vergleich einfach "dumm" zu sein.


Es könnte sein , bemerkenswert, dass Ihr Algorithmus nicht korrekt für das Jahr 2100. ist en.wikipedia.org/wiki/Leap_year#Algorithm

2
Ich weiß ;-) Allerdings war es für das Jahr 2000 korrekt. Ich spiele nur "spot the pedant" ...
Roddy

Finde den Pedanten! LOL.
jcollum

Ja. Vermeiden Sie das ganze Problem, stützen Sie das Array, wie Sie möchten.
Loren Pechtel

Es würde mich nicht wundern, wenn Ihr durchschnittlicher Pascal-Compiler bei der Generierung des Maschinencodes Jan = 0, Dec = 11 zuweist :-)

4

Der Grund, warum es bei 0 und nicht bei 1 beginnt, ist, dass Sie sich den Versatz so vorstellen können, wie weit vom Anfang des Speichers des Arrays dieses Element entfernt ist. Es heißt nicht, gib mir das 0. Element - es heißt, gib mir das Element, das von Anfang an 0 Elemente ist.

Eine andere Sichtweise ist, dass diese (zum größten Teil) gleichwertig sind:

array[n]

*(array + n)

Der Grund, warum der Standard niemals geändert wird, ist, dass C seit ungefähr 40 Jahren existiert. Es gibt keinen zwingenden Grund, dies zu ändern, und wenn dies der Fall wäre, würde der gesamte vorhandene Code, der davon abhängt, dass der Anfang des Arrays 0 ist, beschädigt.


In der Tat können Sie array[n]wie n[array]in C umschreiben . Es ist keine gute Idee, es zu tun, es ist verwirrend! Aber es ist legal (na ja, zumindest bis C89), weil diese Identität oben und die Tatsache, dass Addition kommutativ ist.
Donal Fellows

Das ist eine verrückte Art, das zu schreiben - etwas, das Sie in einem Code sehen, den Sie pflegen müssen, wäre ein riesiges Warnsignal. Zum Glück bin ich noch nicht darauf
gestoßen

4

Code, der einige Informationen zur ursprünglichen Position / relativen Position enthält, ist viel sauberer, wenn Arrays bei 0 beginnen.

Zum Beispiel: Der Code zum Kopieren eines Vektors an einer definierten Position in einem größeren Vektor ist ein Schmerz mit Arrays, die bei 1 beginnen:

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

Durch Opposition mit Arrays ab 0:

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

Wenn Sie anfangen, große Windungsformeln zu schreiben, ist dies ein Muss.


3

Warum nicht 2 oder 3 oder 20? Es ist nicht so, als ob 1-basierte Arrays einfacher oder einfacher zu verstehen wären als null-basierte Arrays. Um zu 1-basierten Arrays zu wechseln, müsste jeder Programmierer lernen, wie er mit Arrays arbeitet.

Wenn Sie mit Offsets in vorhandenen Arrays arbeiten, ist dies auch sinnvoller. Wenn Sie 115 Bytes aus einem Array gelesen haben, wissen Sie, dass der nächste Block bei 115 beginnt. Und so weiter, das nächste Byte ist immer die Größe der Bytes, die Sie gelesen haben. Bei 1-based müssten Sie immer eine hinzufügen.

Und manchmal müssen Sie mit Datenblöcken in Arrays umgehen, auch in einer Sprache ohne "echte" Zeigerarithmetik. In Java können sich Daten in Speicherzuordnungsdateien oder Puffern befinden. In diesem Fall wissen Sie, dass Block i die Größe * i hat. Bei einem 1-basierten Index wäre dies der Block * i + 1.

Bei einer 1-basierten Indizierung würden viele Techniken überall +1 erfordern.


Warum nicht 2 oder 3 oder 20? Weil 0 die additive Identität und 1 die multiplikative Identität ist. Sie machen am meisten Sinn.

3

Transformieren Sie mit 1-basierten Arrays ein eindimensionales Array in ein mehrdimensionales Array:

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];

Beachten Sie, dass Ihre y- und z-Indizes 0-basiert sind (y - 1, z - 1), auch wenn Ihr Array 1-basiert ist. Unter bestimmten Umständen können Sie 0-basierte Indizes nicht vermeiden. Verwenden Sie aus Gründen der Konsistenz nicht immer 0-basierte Indizes.


3

Warum sollen Arrays um eins beginnen?

Wenn Sie sagen a[x][y], übersetzt der Compiler dies in: a+(x*num_cols+y). Wenn Arrays um eins gestartet würden, würde dies werden a+(x*num_cols+y-1). Dies wäre jedes Mal eine zusätzliche Rechenoperation, wenn Sie auf ein Array-Element zugreifen möchten. Warum sollten Sie Programme verlangsamen wollen?


1
eigentlich müsste es ein + ((x - 1) * num_cols) + y - 1) werden - sowohl x als auch y würden von 1 beginnen.
Dennis Munsie

2

Ich werde hier auf ein Glied treten und etwas anderes vorschlagen als ein ganzzahliges 'keyed' Array.

Ich glaube, Ihr Kollege schafft es, eine Eins-zu-Eins-Abbildung eines 'Sets' in der physischen Welt zu erstellen, in der wir immer mit 1 beginnen wenn Sie 1 zu 1 zwischen Software und der physischen Welt abgebildet sind.

Mein Vorschlag

Verwenden Sie keine ganzzahligen Arrays für das, was Sie speichern, sondern eine andere Art von Wörterbuch oder Schlüsselwertpaar. Diese werden dem realen Leben besser zugeordnet, da Sie nicht an eine beliebige Ganzzahl gebunden sind. Dies hat seinen Platz und ich würde empfehlen, es so oft wie möglich zu verwenden, da die Mapping-Konzepte 1 zu 1 zwischen Software und der physischen Welt von Vorteil sind.

dh kvp['Name Server'] = "ns1.example.com"; (Dies ist nur eines von Millionen möglichen Beispielen).

Discaimer

Dies funktioniert definitiv nicht, wenn Sie mit Konzepten arbeiten, die auf Mathematik basieren, da Mathematik der tatsächlichen Implementierung eines Computers näher ist. Die Verwendung von kvp-Sets wird hier nichts nützen, aber tatsächlich die Dinge durcheinander bringen und es problematischer machen. Ich habe nicht alle Eckfälle durchdacht, in denen etwas besser als kvp oder als Array funktionieren könnte.

Die Endidee ist, nullbasierte Arrays oder Schlüsselwertpaare zu verwenden, wenn es sinnvoll ist. Denken Sie daran, dass jedes Problem wie ein Nagel aussieht, wenn Sie nur einen Hammer haben.


2

Persönlich ist das eine Argument, wenn Array-Indizes als Offsets betrachtet werden. Es macht einfach Sinn.

Man könnte sagen, dass es das erste Element ist, aber der Versatz des ersten Elements relativ zum Ursprung des Arrays ist Null. Wenn Sie also den Array-Ursprung nehmen und Null addieren, erhalten Sie das erste Element.

Bei der Berechnung ist es also einfacher, Nullen hinzuzufügen, um das erste Element zu finden, als eine hinzuzufügen und dann eine zu entfernen.

Ich denke, jeder, der ein paar Sachen auf niedrigerem Level gemacht hat, denkt immer den Null-Weg. Und die Leute, die anfangen oder an höhere Level gewöhnt sind, wünschen sich oft nicht-algorithmische Programmierung für ein Basis-Ein-System. Oder vielleicht sind wir nur durch vergangene Erfahrungen voreingenommen.


Genau - es handelt sich im Grunde genommen um eine Konvention, die aus Sprachen niedrigerer Ebene stammt.

2

Die beiden einzigen (sehr) schwerwiegenden Gründe für die Verwendung von 0-basierten Indizes anstelle von 1-basierten Indizes scheinen zu vermeiden, dass viele Programmierer UND aus Gründen der Abwärtskompatibilität neu ausgebildet werden .

In allen Antworten, die Sie erhalten haben, habe ich keine weiteren ernsthaften Argumente gegen 1-basierte Indizes gesehen.

Tatsächlich basieren Indizes von Natur aus auf 1 , und hier ist der Grund dafür.

Zuerst müssen wir fragen: Woher kommen Arrays? Haben sie reale Entsprechungen? Die Antwort lautet: Ja, so modellieren wir Vektoren und Matrizen in der Informatik. Vektoren und Matrizen sind jedoch mathematische Konzepte, die vor dem Computerzeitalter auf 1 basierende Indizes verwendeten (und die heutzutage immer noch meistens auf 1 basierende Indizes verwenden).

In der realen Welt sind Indizes 1-Basen.

Wie Thomas oben sagte, verwenden Sprachen, die Indizes mit 0 Basen verwendeten, tatsächlich Offsets , keine Indizes. Entwickler, die diese Sprachen verwenden, denken an Offsets und nicht an Indizes. Dies wäre kein Problem, wenn die Dinge klar formuliert wären, aber das sind sie nicht. Viele Entwickler, die Offsets verwenden, sprechen immer noch über Indizes. Und viele Entwickler, die Indizes verwenden, wissen immer noch nicht, dass C, C ++, C #, ... Offsets verwenden.

Dies ist ein Wortlautproblem .

(Anmerkung zu Diskstras Artikel - Er sagt genau das, was ich oben gesagt habe : Mathematiker verwenden 1-basierte Indizes . Diskstra ist jedoch der Meinung, dass Mathematiker sie nicht verwenden sollten, da ein Ausdruck dann hässlich wäre (z. B .: 1 <= n <= 0) Nun, ich bin mir nicht sicher, ob er damit Recht hat. Einen solchen Paradigmenwechsel durchzuführen, um diese außergewöhnlichen leeren Sequenzen zu vermeiden, scheint eine Menge Ärger für ein kleines Ergebnis zu sein.


2
Mathematiker verwenden nicht immer 1-basierte Indizes. Ich habe gesehen, wie x0 häufig für den Anfangswert einer Sequenz verwendet wurde. Es kommt darauf an, was bequemer ist.

2

Haben Sie sich jemals über das "20. Jahrhundert" geärgert, das sich auf die 1900er Jahre bezieht? Nun, es ist eine gute Analogie für die mühsamen Dinge, mit denen Sie sich bei der Verwendung von 1-basierten Arrays ständig befassen.

Betrachten Sie eine allgemeine Array-Aufgabe wie die Lesemethode .net IO.stream:

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

Ich schlage vor, dass Sie Folgendes tun, um sich davon zu überzeugen, dass 0-basierte Arrays besser sind:

Schreiben Sie in jeden Indexierungsstil eine BufferedStream- Klasse, die das Lesen unterstützt. Sie können die Definition der Lesefunktion für die 1-basierten Arrays ändern (z. B. eine Untergrenze anstelle eines Offsets verwenden). Sie brauchen nichts Besonderes, machen Sie es einfach.

Welche dieser Implementierungen ist einfacher? Welche hat hier und da +1 und -1 Offsets gestreut? Das ist was ich dachte. Tatsächlich würde ich argumentieren, dass der einzige Fall, in dem der Indexierungsstil keine Rolle spielt, darin besteht, dass Sie etwas hätten verwenden sollen, das kein Array ist, wie z. B. ein Set.


Es ist eine schlechte Analogie, die Integer-Logik mit Gleitkomma verwechselt.

2

Das liegt daran, wie Arrays aufgebaut sind. Es macht wenig Sinn, von einem zu beginnen. Ein Array ist eine Basisadresse im Speicher, eine Größe und ein Index. Um auf das n-te Element zuzugreifen, ist es:

base + n * element_size

0 ist also offensichtlich der erste Versatz.


1

Es gibt tatsächlich verschiedene Möglichkeiten, dies zu implementieren:

  • 0-basierte Array-Indizes
  • 1-basierte Array-Indizes
  • entweder 0 oder 1 basierte Arrays (wie VB 6.0 ... das ist wirklich schrecklich)

Letztendlich denke ich nicht, dass es wichtig ist, ob eine Sprache 0 oder 1 basierte Arrays verwendet. Ich denke jedoch, dass die beste Wahl die Verwendung von 0-basierten Arrays ist, da die meisten Programmierer an diese Konvention gewöhnt sind und sie mit der überwiegenden Mehrheit des bereits geschriebenen Codes übereinstimmt.

Der einzige Weg, um wirklich etwas falsch zu machen, besteht darin, inkonsistent zu sein wie Visual Basic. Die Codebasis, die ich zurzeit verwalte, ist in 0- und 1-basierte Arrays aufgeteilt. und es ist außerordentlich schwierig herauszufinden, welches welches ist. Dies führt zu einer nervig ausführlichen for-Schleife:

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

hahaha ich erinnere mich, Mann, der saugte ...
Dan Rosenstark

Ich glaube, ich erinnere mich, dass Arrays mit negativen Zahlen beginnen. Nur einer der vielen Gründe, warum ich mich von VB fern halte.

1

Null ist natürlich, wenn über die Position eines Elements in einer linearen Sammlung gesprochen wird.

Stellen Sie sich ein Regal voller Bücher vor - das erste Buch befindet sich bündig mit der Seitenwand des Regals - das ist die Position Null.

Ich denke, es hängt davon ab, ob Sie Array-Indizes als Mittel zum Finden von Dingen oder zum Verweisen auf Dinge betrachten.


1

Ich bevorzuge einen auf 0 basierenden Index, da modulo (und der AND-Operator, wenn er für modulo verwendet wird) für einige Werte immer 0 zurückgibt.

Ich finde mich oft mit Arrays wie folgt:

int blah = array[i & 0xff];

Ich verstehe diese Art von Code oft falsch, wenn ich 1-basierte Indizes verwende.


1

Es ist schwierig, 0-Base zu verteidigen, ohne viel Array-basierten Code zu programmieren, wie z. B. die Suche nach Zeichenfolgen und verschiedene Sortier- / Zusammenführungsalgorithmen, oder mehrdimensionale Arrays in einem eindimensionalen Array zu simulieren. Fortran ist 1-basiert, und Sie brauchen viel Kaffee, um diese Art von Code richtig zu machen.

Aber es geht weit darüber hinaus. Es ist eine sehr nützliche mentale Gewohnheit, über die Länge von etwas nachzudenken, anstatt über die Indizes seiner Elemente. Wenn Sie beispielsweise pixelbasierte Grafiken erstellen, ist es viel klarer, sich Koordinaten als zwischen Pixel fallend vorzustellen, als auf diesen. Auf diese Weise enthält ein 3x3-Rechteck 9 Pixel, nicht 16.

Ein etwas weit hergeholteres Beispiel ist die Idee der Vorausschau beim Parsen oder beim Drucken von Zwischensummen in einer Tabelle. Der "gesunder Menschenverstand" -Ansatz besagt, 1) das nächste Zeichen, Token oder die nächste Tabellenzeile zu erhalten und 2) zu entscheiden, was damit zu tun ist. Der Look-Ahead-Ansatz besagt: 1) Angenommen, Sie können es sehen und entscheiden, ob Sie es wollen, und 2) Wenn Sie es wollen, "akzeptieren" Sie es (wodurch Sie das nächste sehen können). Dann, wenn Sie den Pseudocode ausschreiben, ist es viel einfacher.

Ein weiteres Beispiel ist die Verwendung von "goto" in Sprachen, in denen Sie keine Wahl haben, z. B. in MS-DOS-Batchdateien. Der "gesunder Menschenverstand" -Ansatz besteht darin, zu erledigende Codeblöcke mit Etiketten zu versehen und als solche zu kennzeichnen. Oft ist es besser, Beschriftungen an den Enden von Codeblöcken anzubringen, um sie zu überspringen. Dies macht es "strukturiert" und viel einfacher zu ändern.


1

Es ist einfach so und das schon seit vielen Jahren. Es zu ändern oder sogar zu debattieren, ist ebenso sinnlos wie sich zu ändern oder über sich ändernde Ampeln zu debattieren. Machen wir Blau = Stopp, Rot = Los.

Informieren Sie sich über Änderungen, die im Laufe der Zeit in Numerical Recipes for C ++ vorgenommen wurden. Sie hatten Makros verwendet, um 1-basierte Indizierungen zu fälschen, aber in der Ausgabe von 2001 gaben sie auf und schlossen sich der Herde an. Möglicherweise gibt es auf der Website www.nr.com Aufschluss über die Gründe dafür

Übrigens nerven auch die Varianten, einen Bereich aus einem Array anzugeben. Beispiel: Python vs. IDL; a [100: 200] vs a [100: 199], um 100 Elemente zu erhalten. Ich muss nur die Macken jeder Sprache lernen. Eine Sprache zu ändern, die sich auf die eine oder andere Weise anpasst, würde ein solches Zähneknirschen verursachen und kein wirkliches Problem lösen.


1

Ich bevorzuge 0-basierte Arrays, da dies, wie von anderen erwähnt, die Mathematik erleichtert. Wenn wir zum Beispiel ein eindimensionales Array von 100 Elementen haben, die ein 10x10-Gitter emulieren, wie lautet dann der Array-Index i des Elements in Zeile r, Spalte c:

0-basiert: i = 10 * r + c
1-basiert: i = 10 * (r - 1) + c

Und angesichts des Index i lautet die Rückkehr zur Zeile und Spalte:

0-basiert: c = i% 10
         r = Etage (i / 10)
1-basiert: c = (i - 1)% 10 + 1
         r = ceil (i / 10)

Angesichts der Tatsache, dass die obigen Berechnungen bei der Verwendung von 1-basierten Arrays deutlich komplexer sind, erscheint es logisch, 0-basierte Arrays als Standard zu wählen.

Ich denke jedoch, dass jemand behaupten könnte, dass meine Logik fehlerhaft ist, da ich davon ausgehe, dass es einen Grund geben würde, 2D-Daten in einem 1D-Array darzustellen. Ich bin in C / C ++ auf eine Reihe solcher Situationen gestoßen, muss aber zugeben, dass das Ausführen solcher Berechnungen in gewisser Weise sprachabhängig ist. Wenn Arrays die gesamte Indexberechnung für den Client wirklich zu jeder Zeit durchgeführt haben, kann der Compiler Ihre M-basierten Array-Zugriffe zur Kompilierungszeit einfach in 0-basierte umwandeln und alle diese Implementierungsdetails vor dem Benutzer verbergen. Tatsächlich könnte jede Konstante zur Kompilierungszeit verwendet werden, um denselben Satz von Operationen auszuführen, obwohl solche Konstrukte wahrscheinlich nur zu unverständlichem Code führen würden.

Vielleicht wäre ein besseres Argument, dass die Minimierung der Anzahl der Arrayindexoperationen in einer Sprache mit 1-basierten Arrays die Ausführung einer Ganzzahldivision unter Verwendung der Ceiling-Funktion erfordern würde. Aus mathematischer Sicht sollte die Ganzzahldivision jedoch d Rest r zurückgeben, wobei d und r beide positiv sind. Daher sollten 0-basierte Arrays verwendet werden, um die Mathematik zu vereinfachen.

Wenn Sie beispielsweise eine Nachschlagetabelle mit N Elementen generieren, ist der nächste Index vor dem aktuellen Wert im Array für den Wert x (wobei Werte, bei denen das Ergebnis vor dem Runden eine Ganzzahl ist, ungefähr ignoriert werden):

0-basiert mit Etage: Etage ((N - 1) * x / xRange)
1-basiert mit Etage: Etage ((N - 1) * x / xRange) + 1
1-basiert mit ceil: ceil ((N - 1) * x / xRange)

Beachten Sie, dass bei Verwendung der Standardkonvention zum Abrunden von Arrays auf 1-Basis eine zusätzliche Operation erforderlich ist, die unerwünscht ist. Diese Art von Mathematik kann vom Compiler nicht ausgeblendet werden, da hier weniger Kenntnisse darüber erforderlich sind, was hinter den Kulissen passiert.


Dies ist ein guter Grund, bis Sie über höhere Sprachen verfügen, die mehrdimensionale Arrays unterstützen.

1

Ich wette, der Programmierer war nur verärgert über die Gegenintuitivität eines 0-basierten Arrays im täglichen Denken und plädierte für ein intuitiveres Mittel zur Beschreibung von Arrays. Ich finde es ironisch, dass wir als Menschen so viel Zeit damit verbracht haben, "Klassen" zu entwickeln, damit wir die Dinge in unserem Code menschlicher beschreiben können, aber wenn wir uns Arrays mit 0 gegen 1 ansehen, scheinen wir hängen zu bleiben die Logik davon allein.

In Bezug auf den Computer und mathematisch gesehen wird 0 wahrscheinlich besser sein, aber ich habe das Gefühl, dass hier ein Punkt übersehen wird. Wenn wir die Dinge menschlicher beschreiben wollten (z. B. Klassen), warum wollten wir das nicht auch für andere Teile der Sprache? Ist dies nicht gleichermaßen logisch oder gültig (oder hat diesbezüglich eine höhere Priorität ...), um eine Sprache für den Menschen leichter verständlich und benutzerfreundlicher zu machen und damit weniger anfällig für Szenarien, die dazu neigen, Logikfehler zu erzeugen und anfälliger zu sein? zur schnelleren Herstellung einer brauchbaren Kreation. PHP Beispiel:

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

gibt ein 1-basiertes Array pro Anfrage an.

Warum nicht die Norm haben:

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

Und die Ausnahme sein:

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

Im Falle von PHP ist mein Einsatz 80% der Zeit, in der ein 1-basiertes Array die Standardsyntax darstellt, um Logikfehler in realen Anwendungsfällen zu verringern oder zumindest im Durchschnitt nicht mehr zu verursachen, während es zu einer Menge wird Es ist für den Codierer einfacher, verwendbaren Code schneller zu erstellen. Denken Sie daran, ich gehe davon aus, dass es immer noch die Option Array (0 => 'value') gibt, wenn es benötigt wird, aber ich gehe auch davon aus, dass es die meiste Zeit praktisch ist, etwas zu haben, das näher an einer Beschreibung der realen Welt liegt.

Das klingt wirklich nicht allzu weit hergeholt von einer Anfrage, wenn man sie aus dieser Perspektive betrachtet. Wenn wir uns einer Schnittstelle nähern, sei es einem Betriebssystem oder einer Sprache für einen Programmierer, werden wir in den meisten Fällen umso glücklicher sein und weniger Missverständnisse zwischen Mensch und Computer (menschliche Logik). Bugs), und die schnellere Produktion, etc. werden wir haben. Wenn ich in der realen Welt 80% der Zeit Dinge mit 1 beschreibe, wenn ich Listen erstelle oder zähle, sollte der Computer meine Bedeutung idealerweise so interpretieren, dass er sie mit so wenig Informationen wie möglich versteht oder sich von meiner normalen Art, etwas zu beschreiben, unterscheidet. Kurz gesagt, je näher wir der realen Welt kommen, desto besser ist die Abstraktion. Was er also will, ist keineswegs dumm, da dies das ultimative Ziel ist und ein Beweis für das Bedürfnis nach mehr Abstraktion wäre. Der Computer kann es letztendlich immer noch als eine spezielle Verwendung eines 0-basierten Arrays ansehen. Es ist mir egal, wie der Computer es interpretiert, solange es für mich eine einfachere und intuitivere Möglichkeit ist, mit weniger Fehlern im Laufe der Zeit zu beschreiben, was ich will.

Das sind meine zwei Cent. Ich bezweifle ernsthaft, was er sagte oder was interpretiert wurde und was er meinte. Wahrscheinlich meinte er: "Ich hasse es, dem Computer auf weniger intuitive Weise zu sagen, was ich will." :) Tun wir nicht alle? lol.


1

Es ist möglich, wenn Sie beim Schreiben Ihres "eigenen" Codes aufpassen. Sie können davon ausgehen, dass Ihr Index für alle n> = 0 bei n beginnt, und entsprechend programmieren.

In Bezug auf Standard hat Borealid ein gutes Argument.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.