1818 Stimmen

Warum ist es bei Arrays so, dass a[5] == 5[a]?

Wie Joel unterstreicht in Stack Overflow-Podcast #34 , in Programmiersprache C (auch bekannt als: K & R), wird diese Eigenschaft von Arrays in C erwähnt: a[5] == 5[a]

Joel sagt, dass es an der Zeigerarithmetik liegt, aber ich verstehe es immer noch nicht. Warum ist a[5] == 5[a] ?

2143voto

mmx Punkte 400975

Der C-Standard definiert die [] Operator wie folgt:

a[b] == *(a + b)

Deshalb a[5] auswerten wird:

*(a + 5)

y 5[a] auswerten wird:

*(5 + a)

a ist ein Zeiger auf das erste Element des Arrays. a[5] ist der Wert, der 5 Elemente weiter von a , was dasselbe ist wie *(a + 5) und aus der Grundschulmathematik wissen wir, dass diese gleich sind (Addition ist kommutativ ).

307voto

David Thornley Punkte 55244

Denn der Zugriff auf ein Array ist in Form von Zeigern definiert. a[i] ist definiert als *(a + i) , die kommutativ ist.

291voto

Keith Thompson Punkte 240701

Ich glaube, dass die anderen Antworten etwas übersehen haben.

Ja, p[i] ist per Definition äquivalent zu *(p+i) , was (da die Addition kommutativ ist) äquivalent ist zu *(i+p) , die (wiederum durch die Definition des [] Operator) ist äquivalent zu i[p] .

(Und in array[i] wird der Array-Name implizit in einen Zeiger auf das erste Element des Arrays umgewandelt).

Aber die Kommutativität der Addition ist in diesem Fall nicht ganz so offensichtlich.

Wenn beide Operanden vom gleichen Typ sind, oder sogar von verschiedenen numerischen Typen, die zu einem gemeinsamen Typ promotet werden, ist die Kommutativität absolut sinnvoll: x + y == y + x .

In diesem Fall geht es aber speziell um Zeigerarithmetik, bei der ein Operand ein Zeiger und der andere eine ganze Zahl ist. (Ganzzahl + Ganzzahl ist eine andere Operation, und Zeiger + Zeiger ist Unsinn.)

Die im C-Standard enthaltene Beschreibung der + Betreiber ( N1570 6.5.6) sagt:

Bei der Addition müssen entweder beide Operanden vom arithmetischen Typ sein, oder einer Operand ein Zeiger auf einen vollständigen Objekttyp und der andere muss vom Typ Ganzzahl sein.

Es hätte genauso gut heißen können:

Bei der Addition müssen entweder beide Operanden vom arithmetischen Typ sein, oder die Linke Operand muss ein Zeiger auf einen vollständigen Objekttyp sein und der rechter Operand muss vom Typ Ganzzahl sein.

in diesem Fall sowohl i + p y i[p] wäre illegal.

In C++ haben wir eigentlich zwei Sätze von überladenen + Operatoren, die sich grob wie folgt beschreiben lassen:

pointer operator+(pointer p, integer i);

y

pointer operator+(integer i, pointer p);

von denen nur der erste wirklich notwendig ist.

Warum also ist das so?

C++ hat diese Definition von C geerbt, das sie von B übernommen hat (die Kommutativität der Array-Indizierung wird ausdrücklich in der 1972 Verweis der Nutzer auf B ), die es von BCPL (Handbuch aus dem Jahr 1967), das möglicherweise aus noch früheren Sprachen (CPL? Algol?) stammt.

Die Idee, dass die Indizierung von Arrays durch Addition definiert ist und dass die Addition, selbst von Zeigern und ganzen Zahlen, kommutativ ist, geht also viele Jahrzehnte zurück, bis zu den Vorgängersprachen von C.

Diese Sprachen waren viel weniger stark typisiert als das moderne C. Insbesondere die Unterscheidung zwischen Zeigern und ganzen Zahlen wurde oft ignoriert. (Frühe C-Programmierer verwendeten Zeiger manchmal als ganze Zahlen ohne Vorzeichen, bevor die unsigned Schlüsselwort wurde der Sprache hinzugefügt). Auf die Idee, die Addition nicht kommutativ zu machen, weil die Operanden von unterschiedlichem Typ sind, wären die Entwickler dieser Sprachen also wahrscheinlich nicht gekommen. Wenn ein Benutzer zwei "Dinge" addieren wollte, ganz gleich, ob es sich bei diesen "Dingen" um ganze Zahlen, Zeiger oder etwas anderes handelte, lag es nicht an der Sprache, dies zu verhindern.

Und im Laufe der Jahre hätte jede Änderung dieser Regel zu einem Bruch des bestehenden Codes geführt (obwohl der ANSI C-Standard von 1989 eine gute Gelegenheit gewesen sein könnte).

Wenn man C und/oder C++ dahingehend ändert, dass der Zeiger links und die ganze Zahl rechts stehen muss, könnte das zwar zu einem Bruch im bestehenden Code führen, aber es gäbe keinen Verlust an echter Ausdruckskraft.

Wir haben jetzt also arr[3] y 3[arr] bedeutet genau dasselbe, obwohl die letztere Form niemals außerhalb der IOCCC .

210voto

James Curran Punkte 98228

Und, natürlich

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

Der Hauptgrund dafür war, dass in den 70er Jahren, als C entwickelt wurde, die Computer nicht über viel Speicher verfügten (64 KB war viel), so dass der C-Compiler nicht viel Syntaxprüfung durchführen konnte. Daher " X[Y] " wurde eher blindlings mit " *(X+Y) "

Dies erklärt auch die " += " und " ++ Syntaxen". Alles in der Form " A = B + C " die gleiche kompilierte Form hatte. Wenn aber B dasselbe Objekt wie A war, gab es eine Optimierung auf Assemblerebene. Aber der Compiler war nicht klug genug, um das zu erkennen, also musste der Entwickler ( A += C ). Ähnlich verhält es sich, wenn C war 1 war eine andere Optimierung auf Baugruppenebene verfügbar, und auch hier musste der Entwickler sie explizit angeben, da der Compiler sie nicht erkannte. (In jüngerer Zeit tun dies die Compiler, so dass diese Syntaxen heutzutage weitgehend überflüssig sind).

59voto

user30364 Punkte 614

Eine Sache, die anscheinend niemand erwähnt hat, ist Dinahs Problem mit sizeof :

Man kann nur eine ganze Zahl zu einem Zeiger addieren, nicht aber zwei Zeiger zusammen. Auf diese Weise weiß der Compiler beim Hinzufügen eines Zeigers zu einer Ganzzahl oder einer Ganzzahl zu einem Zeiger immer, welches Bit eine Größe hat, die berücksichtigt werden muss.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X