Warum beginnen Arrays mit dem Index Null?

  Ralf Hersel   Lesezeit: 4 Minuten  🗪 28 Kommentare

Vielleicht habt ihr euch beim Programmieren schon einmal geärgert, dass Arrays mit dem Index Null beginnen, statt mit Eins. Im Artikel findet ihr die Antwort.

warum beginnen arrays mit dem index null?

Im Titelbild seht ihr Edsger W. Dijkstra, den Papst der strukturierten Programmiersprachen und Turing-Award Träger, der die Titelfrage bereits im August 1982 beantwortet hat. Leider kenne ich niemanden, der seine Erklärung verstanden hat. Doch worum geht es überhaupt?

Alle modernen Programmiersprachen kennen den Datentyp Array, als Verbund von gleichartigen Datentypen. In Python wird dieser Datentyp Liste genannt. Hier sind zwei einfache Beispiele in Python:

Zahlen = [1, 2, 3]
Wochentag = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']

Die Elemente einer solchen Liste (Array) lassen sich über einen Index ansprechen, der die Position in der Liste angibt. Auch hierfür ein (falsches) Beispiel:

Zahlen[1] ergibt das erste Element aus der Liste, nämlich die Zahl 1.
Wochentag[2] ergibt das zweite Element aus der Liste, nämlich den Tag 'Di'.

Das stimmt natürlich nicht, zeigt aber die Problematik, dass sich die Indizierung gegen den gesunden Menschenverstand richtet. Tatsächlich ist es so:

Zahlen[0] ergibt das erste Element aus der Liste, nämlich die Zahl 1.
Wochentag[1] ergibt das zweite Element aus der Liste, nämlich den Tag 'Di'.

Die Indizierung beginnt bei Null und nicht bei Eins. Doch warum ist das so?

Zunächst werfe ich einen Blick auf die Menge der natürlichen Zahlen. Sie umfasst entweder die positiven ganzen Zahlen (also ohne die 0)  N = { 1 ; 2 ; 3 ; … } oder die nichtnegativen ganzen Zahlen (also inklusive der 0) N0 = { 0 ; 1 ; 2 ; 3 ; … }. Nun, das hilft uns nicht weiter, weil der Beginn der natürlichen Zahlen nicht eindeutig definiert ist; man kann mit Null oder Eins anfangen.

Man kann auch im Internet recherchieren, um eine Erklärung zu finden. Doch dort finden sich viele Diskussionen; einen Konsens sucht man vergeblich. Am meisten hat mich ein Blick auf die Anfänge der Programmierung überzeugt. Wenn wir zurückdenken an die Zeiten von Assembler, oder auch an die Programmiersprache C, sieht man häufig diese Erklärung:

Ein Array findet seine Repräsentation im Arbeitsspeicher des Computers. Dabei bedeutet der Index den Offset (Versatz), um an die entsprechende Stelle im Speicher zu gelangen. Somit handelt es sich beim Index Null um einen Offset von Null. Damit wird auf die erste Position des Arrays im Speicher verwiesen. Es findet kein Versatz statt.

Offset:   0    1    2    3
Speicher: a    b    c    d

Ein Index von 1 würde einen entsprechenden Versatz um 1 bedeuten und damit auf die nächste (zweite) Stelle des Arrays verweisen.

Für mich wirkt diese Erläuterung überzeugend, auch wenn sie im Altertum der Compiler begründet ist. Bei der Implementierung sparte man damit ein paar Operationen auf der CPU ein. Die Entwickler haben sich über die Jahre an diese Konvention gewöhnt. Hinzukommt, dass Arrays relativ selten über Indizes angesprochen, sondern über Iteratoren behandelt werden. Wer ein ...

for each element in list: do something

... ausführt, muss sich keine Gedanken über die Indizierung machen. Ich gebe jedoch zu, dass ich bei meiner Programmierung schon oft über das plus/minus-Eins Problem gestolpert bin. Ausserdem kann ich mir kaum vorstellen, dass eine Implementierung, bei der der Index bei Eins beginnt, heutzutage messbare Leistungsunterschiede hervorbringen würde. Daher würde ich die alten Zöpfe abschneiden und die Indizierung zukünftig mit Eins beginnen. Das wäre ein Entgegenkommen der Programmiersprachen an den gesunden Menschenverstand. Mir ist klar, dass dann 99 % aller Programme nicht mehr funktionieren werden :)

Tags

Array, Null, Index, Programmierung, Liste

ralf
Geschrieben von ralf am 14. Februar 2024 um 09:25

Also ich hab in ca 30 Jahren Programmierung noch nie ein Problem damit gehabt. :-)

Robert
Geschrieben von Robert am 14. Februar 2024 um 09:27

Ich habe mich beim Programmieren schon einmal geärgert, dass Arrays mit dem Index Eins beginnen, statt mit Null. – Wenn ich doch mal mit R, MATLAB, usw. arbeiten muss. In einem Slice (i:f) ist dann auch noch das letzte Element (f) enthalten.

Understater
Geschrieben von Understater am 14. Februar 2024 um 10:13

Ich habe mich schon oft mit der 1 und der 0 vertan, aber noch nie darüber geärgert. Mir passiert es auch schon mal, dass ich einen Algorithmus schreibe der irgendwann durch Hoch- oder Runterzählen bei einer (noch)nicht definierten Variable landet. Das ist mein Fehler. Nicht der des Werkzeugs.

klt
Geschrieben von klt am 14. Februar 2024 um 11:56

Ich finde die Erklärung von Dijsktra sehr einleuchtend, dass man Ranges oder Segmente von natürlichen Zahlen als links geschlossene und rechts offene Intervalle angeben sollte. Daraus folgt, dass die Menge der Indizes eines Arrays der Länge N entweder als 1≤i

Sigesmund
Geschrieben von Sigesmund am 14. Februar 2024 um 20:02

Ich finde es gut, dass die Arrays mit 0 anfangen. Da ich mehrere Anwendungsfälle habe bei den ich Index berechne, muss ich nicht jedes mal eins addieren. Z.B. benötige ich öfters eine Modulo Berechnung.

Brian90
Geschrieben von Brian90 am 16. Februar 2024 um 06:14

Könntest du mir das bitte mal anhand eines Beispiels erläutern?

Hannes
Geschrieben von Hannes am 18. Februar 2024 um 12:31

Angenommen, du willst eine Liste in eine Tabelle umwandeln. (Früher war das z.B. nötig, wenn du Elemente einer Webseite in eine Tabelle positionieren willst. Heute kann man das mit CSS dem Browser überlassen - aber der Browser-Entwickler muss das so ähnlich machen). Angenommen du möchtest 3 Spalten. 0-basiert schaut das so aus (Listenindex-> Zeile/Spalte): 0 -> 0/0 1 -> 0/1 2 -> 0/2 3 -> 1/0 Damit ist die Zeile die Integerdivision von Listenindex / 3. Und die Spalte der Modulo (Rest) von Listenindex / 3. Mit 1-basierten Indices müsste man von Listenindex zuerst 1 abziehen und dann wieder jeweils für Zeile und Spalte hinzufügen.

Jens T.
Geschrieben von Jens T. am 14. Februar 2024 um 22:51

Ich habe seit Jahrzehnten kein Problem mit dem Basisindex. Im uralten BASIC gibt es sogar einen Befehl, um den Basisindex von Arrays einzustellen: OPTION BASE { 0 | 1 }. Und nebenbei: eine Liste ist oft kein Array. Die Standard-Lib von C++ definiert z.B. std::list, welches keinen Operator [], jedoch einen Iterator hat. std::array hingegen hat einen Operator []. In Python ist die Sache wieder ganz anders. Listen und Arrays können per Index angesprochen werden. Unterschied ist der, dass Arrays nur Variablen gleichen Types beinhalten können. So ganz einfach ist die Sache also nicht. Es ist viel mehr eine Gewöhnung an das, was die jeweilige Sprache als Basisindex definiert. Etwas ähnliches gibt es auch ganz abseits der Computerei: in der atronomischen Zeitrechnung gibt es das Jahr Null, welches es in der traditionellen christlichen Zeitrechnung nicht gibt.

Tril
Geschrieben von Tril am 15. Februar 2024 um 12:01

Äh, also ich meine in IT Geschichte Mal gelernt zu haben, dass bei 0 angefangen wird, um alle Werte eines unsigned-int nutzen zu können. Alle unsigned-Zahlen haben als minimal Wert 0, also wenn man den Index in einer Variable speichern will, ist 0 logischerweise der Anfang. Würde man bei 1 anfangen, würde man eine mögliche Index Position verlieren.

Das Assembler Offset Argument basiert schlussendlich wohl auf dem gleichen Grund, bzw. die Argumentationen ergänzen sich in meinem Augen perfekt.

SkyyySi
Geschrieben von SkyyySi am 15. Februar 2024 um 14:10

Solange man nicht mit einem Array arbeitet, dass 100 % des theoretisch möglichen Adressbereichs ausfüllt (also heutzutage 2^64 Bytes, oder etwa 17 Exabytes), ist das vollkommen egal, ein Compiler könnte hier sehr einfach automatisch einen Versatz berechnen. Der eigentliche Grund ist, dass Arrays ursprünglich einfach nur eine nettere Syntax für Zeigerarithmetik waren. In C ist die Zeile

x = a[3];

identisch zur Zeile

x = *(a + 3);

Auf Deutsch also "x ist der Wert hinter der Speicheradresse von a + 3".

Es wäre sehr verwirrend gewesen, wenn manchmal ein Versatz eingerechnet wird und manchmal nicht. Dann doch lieber immer 0-basierte Indizierung.

Für moderne Programmiersprachen gilt das aber natürlich nicht, hier ist es zum aller größten Teil einfach aus Gewohnheit.

Philipp Petsch
Geschrieben von Philipp Petsch am 15. Februar 2024 um 19:07

Ja, wäre ärgerlich, wenn man extra ein größeren Speicher für den Index braucht, weil int8 zum Beispiel überschritten werden würde, weil man wo 256 Elemente braucht, zum Beispiel bei einer fft. Aber an sich könnte der Interpreter/der Compiler den Code ja so übersetzen, dass es wieder bei 0 beginnt, obwohl der Code selber bei 1 startet.

Christoph
Geschrieben von Christoph am 15. Februar 2024 um 12:24

Also Julia hat, glaube ich, Array-Indizes, die mit 1 anfangen, kann aber auch andere Zählungen festlegen: https://docs.julialang.org/en/v1/devdocs/offset-arrays/ Ich denke, dass es vor allem gut ist, eine einheitliche Linie zu haben, sonst entstehen Verwirrung und zusätzliche Fehler und alles wird letztlich nur komplizierter, jedenfalls solange Menschen programmieren.

Morx
Geschrieben von Morx am 15. Februar 2024 um 12:34

zukünftig mit Eins beginnen: bloss nicht! Alle bestehenden Programme müssten umgeschrieben werden...

Rolf
Geschrieben von Rolf am 15. Februar 2024 um 13:01

In sprachen deren Schwerpunkt numerische Berechnungen sind wie z.B. FORTRAN, APL, R und Julia hat das erste Element den Index 1

Ralf
Geschrieben von Ralf am 15. Februar 2024 um 16:14

Bitte nicht! Ich habe schon in verschiedenen Sprachen mit beiden Konventionen gearbeitet. Für meinen ‚gesunden Menschenverstand‘ sind beide Varianten gleich gut. Der Wechsel aber ist eine Katastrophe. Bloß weil die früher berücksichtigten Argumente keine Auswirkung mehr haben ist das kein Grund dies über den Haufen zu werfen.

Georg Ording
Geschrieben von Georg Ording am 15. Februar 2024 um 23:48

Im Prinzip müssten wir im 10 System immer bei 0 anfangen zu zählen. 0 - 9 = 10 Zahlen 10 - 19 = 10 Zahlen usw. Zähle ich zu 1 angefangen 10 Zahlen hinzu bin ich schon im nächsten Zehnersystem den mit der 1_ vorn. Wenn ich von 10 bis 20 alle Zahlen zähle die enthalten sind bekomme ich 11 Zahlen. Somit wäre es nur richtig das immer bei 0 angefangen würde. Die 0 wider aber erst sehr spät in das Zahlensystem als Zahl aufgenommen wodurch die Menschen schon daran gewöhnt waren mit 1 zu beginnen. Im den PC Systemen gibt es jedoch nur die 0 und die 1 somit macht es hier natürlich Sinn die 0 als erste Zahl zu nutzen.

Dr. Andreas Keibel
Geschrieben von Dr. Andreas Keibel am 16. Februar 2024 um 06:39

Sowas ginge nur in einer ganz neuen Programmiersprache. Vielleicht kann man es auch umgekehrt machen, und im normalen Leben mit 0 beginnen. 🤣. So wie bei den Stockwerken eines Hauses in Deutschland. Erdgeschoss ist 0 und dann folgt der erste Stock. 😁. Frag mal bei einem Wettbewerb nach dem Nullten Platz . Der ist noch vor dem Ersten! 🤣 Würde für ganz schön Verwirrung sorgen, aber auch Sinn machen. Aus Sicht eines Programmierers jedenfalls.

TMII
Geschrieben von TMII am 16. Februar 2024 um 07:12

Man stelle sich einen Zaun vor, bei der jeder Pfosten 1m voneinander entfernt ist. Der erste Pfosten beginnt 10m von der Hauswand entfernt. Wo stehen die Pfosten?

  1. Pfosten: 10 + 1 * 0 = 10 m
  2. Pfosten: 10 + 1 * 1 = 11 m
  3. Pfosten: 10 + 1 * 2 = 12 m
  4. Pfosten: 10 + 1 * 3 = 13 m ... Index 0 ist die mathematisch logische Antwort. Lernt man halt nicht in der Schule und muss man sich dran gewöhnen.
Thalal
Geschrieben von Thalal am 16. Februar 2024 um 08:25

Eine Frage der Sichtweise. Man stelle sich die Liste nicht als Gerade sondern als Kreis vor. Der Index beschreibt wieviel Schritte man gehen muss um an den Wert zu kommen. Um an den zweiten Wert zu kommen muss man 1 Index weiter. Ums an den letzten Index der Liste mit unbekannter Länge zu kommen muss -1 Index weiter. Somit wird das ganze symmetrisch. Ansonsten gäbe es keine 0.

Harry
Geschrieben von Harry am 16. Februar 2024 um 08:36

Überschrift schon falsch.

Sollte lauten:

Warum beginnen Arrays mit dem Index 0

Joe Kolade
Geschrieben von Joe Kolade am 16. Februar 2024 um 09:00

Da in einem Loop über einen Array (und andere Iterables) der Index auch Auskunft darüber liefert wie viele Durchläufe schon stattfanden MUSS das aus meiner Sicht bei 0 beginnen! In meiner Zeit mit C++ in der angewandten Mathematik war der Start bei 0 zum Iterieren essentiell und ein Start bei 1 wäre umständlich.

toprope
Geschrieben von toprope am 16. Februar 2024 um 11:25

In COBOL beginnt das Array bei 0. Am Alter der Programmiersprachen liegt es vermutlich nicht.

HannesK
Geschrieben von HannesK am 16. Februar 2024 um 11:27

Im ersten Lebensjahr war ich 0 Jahre alt. Mein Lineal beginnt mit einer 0, wo die 1 steht, beginnt der zweite Zentimeter. Also den Array-Index nicht als Ordnungszahl lesen. Dann ist die Welt in Ordnung.

Peter
Geschrieben von Peter am 16. Februar 2024 um 13:53

Halte ich nicht viel von.

Lenzi
Geschrieben von Lenzi am 16. Februar 2024 um 14:26

... früher habe ich an der Stelle 0 die Länge des Arrays gespeichert und bin von 1 ... Array(0) iteriert - man muss halt mit Länge+1 den Array anlegen ...

Bart
Geschrieben von Bart am 16. Februar 2024 um 18:20

I think starting with one is a horrible idea. All the multidimensional slicing involving modulo or division operation would be pretty involved to implement.

Jörg Meltzer
Geschrieben von Jörg Meltzer am 16. Februar 2024 um 20:28

$[=1 Oh wait. Sogar der letzte perl hacker hat aufgegeben array start bei index 1 zu unterstützen.

Hannes
Geschrieben von Hannes am 18. Februar 2024 um 12:11

Viele Berufsgruppen haben besondere Konventionen oder Vorgehensweisen, die für einen Laien vielleicht unverständlich erscheinen. Aber ich denke, für einen mathematisch/technisch versierten Menschen sind die 0-basierten Arrays ja total logisch. Spätestens, wenn man mit maschinenahen Sprachen zu tun hat, kann man sich das gar nicht anders vorstellen. Natürlich hatte ich auch schon öfters Fehler, weil ich bei Array- oder String-Positionen um eins daneben gelegen bin. Aber da würde ein 1-basiertes Array nix ändern. Dieses Problem hat man ja nicht bei der Standard-For-Schleife (sofern man die mal verwendet), sondern eher bei spezielleren Fällen. Vorzeitige Schleifenabbrüche, Substring-Operationen, etc. Diese Fehler lassen sich aber leicht finden. Nötigenfalls debuggen oder mit Tests. Problematischer sehe ich die Interoperabilität. Z.B. wenn man Daten eines IOT-Systems verarbeitet, das maschinennah arbeitet und verschiedene Daten sparsam in ein Array stopft. Die Doku ist dann 0-basiert und man muss ständig umdenken. Oder wenn Systeme von zwei Firmen miteinander kommunizieren müssen, die unterschiedliche Konventionen haben. Da ist es dann immer viel aufwändiger die Fehler zu finden. Und wer ist dann schuld ;)