Warum beginnt die Indizierung in 'C' mit Null?

154

Warum beginnt die Indizierung in einem Array mit Null in C und nicht mit 1?

Nitish Pareek
quelle
7
Alles dreht sich um Zeiger!
Medopal
3
mögliches Duplikat von Defend Zero-basierten Arrays
dmckee --- Ex-Moderator Kätzchen
3
Ein Zeiger (Array) ist eine Speicherrichtung und der Index ist ein Versatz dieser Speicherrichtung, so dass das erste Element des Zeigers (Arrays) dasjenige ist, dessen Versatz gleich 0 ist.
D33pN16h7
3
@drhirsch, denn wenn wir eine Reihe von Objekten zählen, zeigen wir zunächst auf ein Objekt und sagen "eins".
Phoog
1
Die Amerikaner zählen die Stockwerke (Stockwerke) eines Gebäudes von einem im Erdgeschoss aus; Die Briten zählen von Null (Erdgeschoss) bis zum ersten Stock, dann zum zweiten Stock usw.
Jonathan Leffler

Antworten:

115

In C ist der Name eines Arrays im Wesentlichen ein Zeiger [siehe jedoch die Kommentare] , ein Verweis auf einen Speicherort, und daher array[n]bezieht sich der Ausdruck auf einen Speicherort, der vom nStartelement entfernt ist. Dies bedeutet, dass der Index als Offset verwendet wird. Das erste Element des Arrays ist genau in dem Speicherort enthalten, auf den sich das Array bezieht (0 Elemente entfernt), daher sollte es als bezeichnet werden array[0].

Für mehr Information:

http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html

Massimiliano Peluso
quelle
20
Der Name eines Arrays ist der Name des Arrays. im Gegensatz zu dem weit verbreiteten Missverständnis, sind Arrays nicht in irgendeinem Sinne Zeiger. Ein Array-Ausdruck (z. B. der Name eines Array-Objekts) wird normalerweise, aber nicht immer , in einen Zeiger auf das erste Element konvertiert. Beispiel: sizeof arrGibt die Größe des Array-Objekts an, nicht die Größe eines Zeigers.
Keith Thompson
Obwohl Sie offensichtlich nicht auf den Kommentar von @ KeithThompson reagiert haben, möchte ich, dass Sie einen offensiveren Kurs verwenden: " In C ist der Name eines Arrays im Wesentlichen ein Zeiger, ein Verweis auf einen Speicherort " - Nein, das ist es nicht . Zumindest nicht aus allgemeiner Sicht. Während Ihre perfekte Antwort die Frage so beantwortet, wie wichtig 0 als Indexstart ist, ist der erste Satz einfach falsch. Ein Array zerfällt nicht immer in einen Zeiger auf sein erstes Element.
RobertS unterstützt Monica Cellio
Zitat aus dem C-Standard (C18), 6.3.2.1/4: " Außer wenn es sich um den Operanden des sizeofOperators oder den unären &Operator handelt oder um ein Zeichenfolgenliteral, das zum Initialisieren eines Arrays verwendet wird, eines Ausdrucks vom Typ" Array " vom Typ "wird in einen Ausdruck mit dem Typ" Zeiger auf Typ "konvertiert, der auf das Anfangselement des Array-Objekts zeigt und kein Wert ist. Wenn das Array-Objekt eine Registerspeicherklasse hat, ist das Verhalten undefiniert. "
RobertS unterstützt Monica Cellio
Auch dieser Zerfall geschieht "impliziter" oder "formaler" als hier vorgeschlagen; Es gibt keinen Zerfall eines Zeigerobjekts im Speicher. Dies ist das Objekt dieser Frage: Wird der Zerfall von Array zu Zeiger in ein Zeigerobjekt geändert? - Bitte bearbeiten Sie Ihre Antwort so, dass sie vollständig korrekt ist.
RobertS unterstützt Monica Cellio
103

Diese Frage wurde vor über einem Jahr gestellt, aber hier geht ...


Über die oben genannten Gründe

Während Dijkstras Artikel (auf den zuvor in einer jetzt gelöschten Antwort verwiesen wurde ) aus mathematischer Sicht sinnvoll ist, ist er für die Programmierung nicht so relevant .

Die Entscheidung der Sprachspezifikation und der Compiler-Designer basiert auf der Entscheidung der Computersystem-Designer, mit der Zählung bei 0 zu beginnen.


Der wahrscheinliche Grund

Zitat aus einem Plädoyer für Frieden von Danny Cohen.

Für jede Basis b werden die ersten nicht negativen Ganzzahlen b ^ N nur dann durch genau N Ziffern (einschließlich führender Nullen) dargestellt, wenn die Nummerierung bei 0 beginnt.

Dies kann recht einfach getestet werden. Nehmen Sie in Basis 2 die 2^3 = 8 8. Zahl:

  • 8 (binär: 1000), wenn wir mit 1 beginnen
  • 7 (binär: 111), wenn wir mit 0 beginnen

111kann mit 3Bits dargestellt werden, 1000erfordert jedoch ein zusätzliches Bit (4 Bits).


Warum ist das relevant?

Computerspeicheradressen haben 2^NZellen, die durch NBits adressiert sind . Wenn wir jetzt bei 1 zählen, 2^Nbenötigen N+1die Zellen Adressleitungen. Das Extra-Bit wird benötigt, um auf genau 1 Adresse zuzugreifen. ( 1000im obigen Fall.) Eine andere Möglichkeit, dies zu lösen, besteht darin, die letzte Adresse nicht zugänglich zu lassen und NAdresszeilen zu verwenden.

Beides sind suboptimale Lösungen im Vergleich zur Startzählung bei 0, bei der alle Adressen über exakte NAdresszeilen zugänglich bleiben !


Fazit

Die Entscheidung, mit der Zählung zu beginnen 0, hat seitdem alle digitalen Systeme , einschließlich der darauf ausgeführten Software, durchdrungen , da der Code einfacher in das übersetzt werden kann, was das zugrunde liegende System interpretieren kann. Wenn dies nicht der Fall wäre, würde es für jeden Array-Zugriff eine unnötige Übersetzungsoperation zwischen der Maschine und dem Programmierer geben. Es erleichtert das Kompilieren.


Zitat aus der Zeitung:

Geben Sie hier die Bildbeschreibung ein

Anirudh Ramanathan
quelle
2
Was wäre, wenn sie gerade das Bit 0 entfernt hätten
?
2
Schlagen Sie tatsächlich eine Änderung der Grundrechenarten vor, um sie anzupassen? Denken Sie nicht, dass das, was wir heute haben, eine weitaus bessere Lösung ist?
Anirudh Ramanathan
Jahre später meine 2 cnt wert. Nach meiner Erfahrung (~ 35 Jahre Programmierung) kommt die modulo oder modulare Additionsoperation in der einen oder anderen Form überraschend oft vor. Bei einer Basis von Null ist die nächste in Folge (i + 1)% n, aber bei einer Basis 1 ist es (i-1)% n) +1, daher denke ich, dass eine Basis von 0 bevorzugt wird. Dies taucht ziemlich oft in Mathematik und Programmierung auf. Vielleicht bin es nur ich oder das Gebiet, in dem ich arbeite.
Nyholku
Obwohl alle guten Gründe ich denke, ist es viel einfacher: a[b]wurde wie *(a+b)in frühen Compilern implementiert . Auch heute kann man noch schreiben 2[a]statta[2] . Wenn die Indizes nicht bei 0 beginnen a[b]würden, würden sie sich in verwandeln *(a+b-1). Dies hätte 2 zusätzliche CPUs der Zeit anstelle von 0 erforderlich gemacht, was die Hälfte der Geschwindigkeit bedeutet. Offensichtlich nicht wünschenswert.
Goswin von Brederlow
1
Nur weil Sie 8 Zustände wollen, heißt das nicht, dass Sie die Nummer 8 in ihnen haben müssen. Die Lichtschalter in meinem Haus repräsentieren gerne die Zustände "Licht an" und "Licht aus", ohne sich jemals zu fragen, warum sie nicht die Nummer 2 darstellen.
Spyryto
27

Denn 0 ist die Entfernung vom Zeiger auf den Kopf des Arrays zum ersten Element des Arrays.

Erwägen:

int foo[5] = {1,2,3,4,5};

Um auf 0 zuzugreifen, tun wir:

foo[0] 

Aber foo zerfällt in einen Zeiger, und der obige Zugriff hat eine analoge Zeigerarithmetik, um darauf zuzugreifen

*(foo + 0)

Heutzutage wird die Zeigerarithmetik nicht mehr so ​​häufig verwendet. Vor langer Zeit war es jedoch eine bequeme Möglichkeit, eine Adresse zu nehmen und X "Ints" von diesem Startpunkt weg zu bewegen. Wenn Sie einfach dort bleiben möchten, wo Sie sind, fügen Sie einfach 0 hinzu!

Doug T.
quelle
23

Weil der 0-basierte Index ...

array[index]

... implementiert werden als ...

*(array + index)

Wenn der Index auf 1 basieren würde, müsste der Compiler Folgendes generieren: *(array + index - 1)und dieses "-1" würde die Leistung beeinträchtigen.

Branko Dimitrijevic
quelle
4
Sie sprechen einen interessanten Punkt an. Dies kann die Leistung beeinträchtigen. Aber wird der Leistungseinbruch signifikant sein, um die Verwendung von 0 als Startindex zu rechtfertigen? Ich bezweifle das.
Vorname Nachname
3
@FirstNameLastName 1-basierte Indizes bieten keinen Vorteil gegenüber 0-basierten Indizes, weisen jedoch eine (geringfügig) schlechtere Leistung auf. Dies rechtfertigt 0-basierte Indizes, egal wie "klein" die Verstärkung ist. Selbst wenn 1-basierte Indizes einen gewissen Vorteil bieten, liegt es im Sinne von C ++, Leistung vor Komfort zu wählen. C ++ wird manchmal in Kontexten verwendet, in denen es auf die Leistung ankommt, und diese "kleinen" Dinge können sich schnell summieren.
Branko Dimitrijevic
Ja, ich verstehe, dass kleine Dinge sich summieren und manchmal zu einer großen Sache werden können. Zum Beispiel ist 1 Dollar pro Jahr nicht viel Geld. Aber wenn 2 Milliarden Menschen es spenden, können wir viel Gutes für die Menschheit tun. Ich suche nach einem ähnlichen Beispiel für die Codierung, das zu einer schlechten Leistung führen kann.
Vorname Nachname
2
Anstatt 1 zu subtrahieren, sollten Sie die Adresse des Arrays-1 als Basisadresse verwenden. Das haben wir in einem Compiler gemacht, an dem ich einmal gearbeitet habe. Dadurch entfällt die Laufzeitsubtraktion. Wenn Sie einen Compiler schreiben, sind diese zusätzlichen Anweisungen sehr wichtig. Der Compiler wird verwendet, um Tausende von Programmen zu generieren, von denen jedes tausende Male verwendet werden kann, und dieser zusätzliche 1-Befehl kann in mehreren Zeilen innerhalb einer n-Quadrat-Schleife auftreten. Es kann Milliarden verschwendeter Zyklen ergeben.
Programm
Nein, es wird die Leistung nach dem Kompilieren nicht beeinträchtigen, es wird nur eine kleine Erstellungszeit hinzugefügt, da es am Ende in Maschinencode übersetzt wird. Es wird nur den Compiler-Designern schaden.
Hassaan Akbar
12

Weil es den Compiler und Linker einfacher machte (einfacher zu schreiben).

Referenz :

"... Das Referenzieren des Speichers durch eine Adresse und einen Offset wird auf praktisch allen Computerarchitekturen direkt in der Hardware dargestellt, sodass dieses Designdetail in C die Kompilierung erleichtert."

und

"... das vereinfacht die Implementierung ..."

Progrmr
quelle
1
+1 Ich bin mir nicht sicher, warum die Stimmen nach unten stimmen. Obwohl die Frage nicht direkt beantwortet wird, ist die 0-basierte Indizierung für Menschen oder Mathematiker nicht selbstverständlich. Der einzige Grund dafür ist, dass die Implementierung logisch konsistent (einfach) ist.
Phkahler
4
@phkahler: Der Fehler tritt in Autoren und Sprachen auf, die Array-Indizes als Indizes aufrufen. Wenn Sie es als Offset betrachten, wird 0-basiert auch für Laien selbstverständlich. Betrachten Sie die Uhr, die erste Minute ist als 00:00 geschrieben, nicht als 00:01, nicht wahr?
Lie Ryan
3
+1 - Dies ist wahrscheinlich die richtigste Antwort. C vor Djikistras Papier und war eine der frühesten "Start bei 0" Sprachen. C begann sein Leben "als Assembler auf hoher Ebene" und es ist wahrscheinlich, dass K & R so genau wie im Assembler bleiben wollte, wo Sie normalerweise eine Basisadresse plus einen Offset ab Null haben würden.
James Anderson
Ich dachte, die Frage war, warum 0 basiert verwendet wurde, nicht was besser ist.
Programm
2
Ich werde nicht abstimmen, aber wie das Programm oben kommentiert hat, kann die Basis durch Anpassen der Arrays-Adresse behoben werden, so dass unabhängig von der Basisausführungszeit dieselbe ist und dies im Compiler oder Interpreter trivial zu implementieren ist, was die Implementierung nicht wirklich vereinfacht . Zeugen Pascal, wo Sie jeden Bereich für die Indizierung von IIRC verwenden können, es ist 25 Jahre her;)
Nyholku
5

Der Array-Index beginnt immer mit Null. Nehmen wir an, die Basisadresse ist 2000. Jetzt arr[i] = *(arr+i). if i= 0Dies bedeutet nun , dass *(2000+0) gleich der Basisadresse oder der Adresse des ersten Elements im Array ist. Dieser Index wird als Offset behandelt, sodass der Standardindex bei Null beginnt.

Amit Prakash
quelle
5

Aus dem gleichen Grund, wenn es Mittwoch ist und jemand Sie fragt, wie viele Tage bis Mittwoch Sie 0 statt 1 sagen, und wenn es Mittwoch ist und jemand Sie fragt, wie viele Tage bis Donnerstag Sie 1 statt 2 sagen.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
6
Ihre Antwort scheint nur eine Ansichtssache zu sein.
Heltonbiker
6
Nun, es ist das, was das Hinzufügen von Indizes / Offsets zum Funktionieren bringt. Wenn beispielsweise "heute" 0 und "morgen" 1 ist, ist "morgen morgen" 1 + 1 = 2. Aber wenn "heute" 1 und "morgen" 2 ist, ist "morgen morgen" nicht 2 + 2. In Arrays tritt dieses Phänomen immer dann auf, wenn Sie einen Unterbereich eines Arrays als eigenständiges Array betrachten möchten.
R .. GitHub STOP HELPING ICE
7
Eine Sammlung von 3 Dingen "3 Dinge" zu nennen und sie mit 1,2,3 zu nummerieren, ist kein Mangel. Die Nummerierung mit einem Versatz vom ersten ist selbst in der Mathematik nicht selbstverständlich. Das einzige Mal, wenn Sie in Mathematik von Null indizieren, ist, wenn Sie so etwas wie die nullte Potenz (konstanter Term) in ein Polynom aufnehmen möchten.
Phkahler
9
Betreff: "Die Nummerierung von Arrays, die mit 1 statt mit 0 beginnen, ist für Personen mit einem schwerwiegenden Mangel an mathematischem Denken gedacht." Meine Ausgabe von CLRs "Introduction to Algorithms" verwendet eine 1-basierte Array-Indizierung. Ich glaube nicht, dass die Autoren einen Mangel an mathematischem Denken haben.
RexE
Nein, ich würde sagen, der siebte befindet sich auf Index 6 oder 6 Positionen vom ersten entfernt.
R .. GitHub STOP HELPING ICE
2

Die eleganteste Erklärung, die ich für die nullbasierte Nummerierung gelesen habe, ist die Beobachtung, dass Werte nicht an den markierten Stellen in der Zahlenreihe, sondern in den Zwischenräumen zwischen ihnen gespeichert werden. Das erste Element wird zwischen null und eins gespeichert, das nächste zwischen eins und zwei usw. Das n-te Element wird zwischen N-1 und N gespeichert. Ein Bereich von Elementen kann unter Verwendung der Zahlen auf beiden Seiten beschrieben werden. Einzelne Artikel werden gemäß Konvention mit den Zahlen darunter beschrieben. Wenn man einen Bereich (X, Y) erhält, bedeutet das Identifizieren einzelner Zahlen unter Verwendung der folgenden Nummer, dass man das erste Element ohne Verwendung einer Arithmetik identifizieren kann (es ist Element X), aber man muss eine von Y subtrahieren, um das letzte Element (Y) zu identifizieren -1). Das Identifizieren von Elementen anhand der obigen Nummer würde es einfacher machen, das letzte Element in einem Bereich zu identifizieren (es wäre Element Y).

Obwohl es nicht schrecklich wäre, Elemente anhand der darüber liegenden Zahl zu identifizieren, funktioniert es im Allgemeinen besser, das erste Element im Bereich (X, Y) als das über X zu definieren, als es als das unter (X +) zu definieren 1).

Superkatze
quelle
1

Der technische Grund könnte sich aus der Tatsache ergeben, dass der Zeiger auf einen Speicherort eines Arrays der Inhalt des ersten Elements des Arrays ist. Wenn Sie den Zeiger mit einem Index von eins deklarieren, addieren Programme normalerweise den Wert eins zum Zeiger, um auf den Inhalt zuzugreifen, der natürlich nicht Ihren Wünschen entspricht.

rauben
quelle
1

Versuchen Sie, mit X-, Y-Koordinaten auf einer 1-basierten Matrix auf einen Pixelbildschirm zuzugreifen. Die Formel ist äußerst komplex. Warum ist komplex? Weil Sie am Ende die X- und Y-Koordinaten in eine Zahl umwandeln, den Offset. Warum müssen Sie X, Y in einen Offset konvertieren? Denn so ist der Speicher in Computern als kontinuierlicher Strom von Speicherzellen (Arrays) organisiert. Wie gehen Computer mit Array-Zellen um? Verwenden von Offsets (Verschiebungen aus der ersten Zelle, ein auf Null basierendes Indexierungsmodell).

Irgendwann im Code müssen Sie (oder der Compiler) die 1-Basis-Formel in eine 0-basierte Formel konvertieren, da Computer so mit Speicher umgehen.

Gianluca Ghettini
quelle
1

Angenommen, wir möchten ein Array der Größe 5 erstellen.
Int array [5] = [2,3,5,9,8]

Lassen Sie das erste Element des Arrays auf Position 100 zeigen

und betrachten Sie, dass die Indizierung bei 1 beginnt und nicht bei 0.

Jetzt müssen wir die Position des 1. Elements mit Hilfe des Index ermitteln
(denken Sie daran, dass die Position des 1. Elements 100 ist),

da die Größe einer Ganzzahl 4 Bit beträgt.
> Wenn Sie Index 1 berücksichtigen, wäre die Position die
Größe von Index (1) * Größe der ganzen Zahl (4) = 4,
so dass die tatsächliche Position, die es uns zeigt,

100 + 4 = 104 ist

Dies ist nicht wahr, da die anfängliche Position bei 100 lag.
Es sollte auf 100 zeigen, nicht auf 104.
Dies ist falsch.

Nehmen wir nun an, wir haben die Indizierung von 0 genommen,
dann sollte die
Position des 1. Elements die
Größe des Index (0) * Größe der Ganzzahl sein (4) = 0

daher ->
Position des 1. Elements ist 100 + 0 = 100

und das war die tatsächliche Position des Elements
deshalb beginnt die Indizierung bei 0;

Ich hoffe, es wird Ihren Standpunkt klarstellen.

sahil singh
quelle
1

Ich komme aus Java. Ich habe die Antwort auf diese Frage in der folgenden Abbildung dargestellt, die ich in einem selbsterklärenden Blatt Papier geschrieben habe

Hauptschritte:

  1. Referenz erstellen
  2. Instanziierung des Arrays
  3. Zuordnung von Daten zum Array

  • Beachten Sie auch, wenn das Array gerade instanziiert wird. Standardmäßig wird allen Blöcken Null zugewiesen, bis wir einen Wert dafür zuweisen
  • Das Array beginnt mit Null, da die erste Adresse auf die Referenz zeigt (i: e - X102 + 0 im Bild).

Geben Sie hier die Bildbeschreibung ein

Hinweis : Die im Bild gezeigten Blöcke sind Speicherdarstellungen

Devrath
quelle
0

Zunächst müssen Sie wissen, dass Arrays intern als Zeiger betrachtet werden, da der "Name des Arrays selbst die Adresse des ersten Elements des Arrays enthält".

ex. int arr[2] = {5,4};

Beachten Sie, dass das Array bei Adresse 100 beginnt, sodass das erste Element bei Adresse 100 und das zweite bei 104 liegt. Wenn der Array-Index bei 1 beginnt, ist dies der Fall

arr[1]:-

Dies kann in den Zeigerausdruck wie folgt geschrieben werden:

 arr[1] = *(arr + 1 * (size of single element of array));

Angenommen, die Größe von int beträgt jetzt 4 Byte.

arr[1] = *(arr + 1 * (4) );
arr[1] = *(arr + 4);

Wie wir wissen, enthält der Array-Name die Adresse seines ersten Elements.

arr[1] = *(100 + 4);
arr[1] = *(104);

was gibt,

arr[1] = 4;

Aufgrund dieses Ausdrucks können wir nicht auf das Element unter der Adresse 100 zugreifen, die das offizielle erste Element ist.

Betrachten Sie nun, dass der Array-Index bei 0 beginnt

arr[0]:-

Dies wird als gelöst

arr[0] = *(arr + 0 + (size of type of array));
arr[0] = *(arr + 0 * 4);
arr[0] = *(arr + 0);
arr[0] = *(arr);

Jetzt wissen wir, dass der Array-Name die Adresse seines ersten Elements enthält.

arr[0] = *(100);

das gibt korrektes Ergebnis

arr[0] = 5;

Daher beginnt der Array-Index in c immer bei 0.

Referenz: Alle Details sind in Buch "Die C-Programmiersprache von Brian Kerninghan und Dennis Ritchie" geschrieben.

Akshay Chaudhari
quelle
0

Im Array gibt der Index den Abstand zum Startelement an. Das erste Element befindet sich also in einem Abstand von 0 vom Startelement. Deshalb beginnt das Array bei 0.

Rishi Raj Tandon
quelle
0

Dies liegt daran address, dass das elementim Array nach rechts zeigen muss . Nehmen wir das folgende Array an:

let arr = [10, 20, 40, 60]; 

Wenden wir uns nun den Beginn der Adresse Wesen betrachten 12und die Größe des elementsei 4 bytes.

address of arr[0] = 12 + (0 * 4) => 12
address of arr[1] = 12 + (1 * 4) => 16
address of arr[2] = 12 + (2 * 4) => 20
address of arr[3] = 12 + (3 * 4) => 24

Wenn dies nicht zero-based der arrayFall wäre, wäre technisch gesehen unsere erste Elementadresse in der , 16die aufgrund ihres Standorts falsch ist 12.

Thalaivar
quelle
-2

Der Array-Name ist ein konstanter Zeiger, der auf die Basisadresse zeigt. Wenn Sie arr [i] verwenden, manipuliert der Compiler ihn als * (arr + i). Da der int-Bereich -128 bis 127 beträgt, glaubt der Compiler, dass -128 bis -1 sind negative Zahlen und 0 bis 128 sind positive Zahlen. Der Array-Index beginnt also immer mit Null.

Niks
quelle
1
Was meinst du mit 'int range is -128 to 127' ? Ein intTyp ist erforderlich, um mindestens einen 16-Bit-Bereich zu unterstützen, und auf den meisten Systemen werden heutzutage 32-Bit unterstützt. Ich denke, Ihre Logik ist fehlerhaft und Ihre Antwort verbessert sich nicht wirklich gegenüber den anderen Antworten, die bereits von anderen Personen gegeben wurden. Ich schlage vor, dies zu löschen.
Jonathan Leffler