Ich habe eine seltsame Erfahrung in der C-Programmierung gemacht. Betrachten Sie diesen Code:
int main(){
int array1[6] = {0, 1, 2, 3, 4, 5};
int array2[6] = {6, 7, 8, 9, 10, 11};
printf("%d\n", array1[-1]);
return 0;
}
Wenn ich dies kompiliere und ausführe, erhalte ich keine Fehler oder Warnungen. Wie mein Dozent sagte, -1
greift der Array-Index auf eine andere Variable zu. Ich bin immer noch verwirrt, warum um alles in der Welt hat eine Programmiersprache diese Fähigkeit? Ich meine, warum negative Array-Indizes zulassen?
programming-languages
arrays
c
Mohammed Fawzan
quelle
quelle
-1
eines Subarrays eine absolut gültige Möglichkeit ist, auf das Element vor diesem Array im größeren Array zu verweisen. Der andere Grund ist, dass das Programm ungültig ist, wenn der Index ungültig ist. In den meisten Implementierungen wird jedoch ein stummes Fehlverhalten und kein Fehler außerhalb des Bereichs angezeigt.Antworten:
Die Array-Indizierungsoperation
a[i]
erhält ihre Bedeutung aus den folgenden Merkmalen von CDie Syntax
a[i]
entspricht*(a + i)
. Es gilt also zu sagen5[a]
, am 5. Element von zu gelangena
.Pointer-Arithmetik , so daß ein gegebener Zeiger
p
und eine ganze Zahl isti
,p + i
der Zeigerp
durch vorgeschobeni * sizeof(*p)
BytesDer Name eines Arrays geht
a
sehr schnell in einen Zeiger auf das 0-te Element von übera
Tatsächlich ist die Array-Indizierung ein Sonderfall der Zeigerindizierung. Da ein Zeiger kann innerhalb eines Arrays, jeden beliebigen Ausdruck an einem beliebigen Ort hinweist , dass sieht aus wie
p[-1]
ist nicht durch Prüfung falsch, und so Compiler nicht (nicht) alle solche Ausdrücke als Fehler betrachten.Ihr Beispiel,
a[-1]
bei dema
es sich tatsächlich um den Namen eines Arrays handelt, ist tatsächlich ungültig. IIRC wird nicht definiert , wenn es gibt ein sinnvolles Zeigerwert als das Ergebnis des Ausdrucks ,a - 1
woa
wissen , ist ein Zeiger auf das 0 - te Element eines Arrays zu sein. Ein cleverer Compiler könnte dies also erkennen und als Fehler markieren. Andere Compiler können weiterhin kompatibel sein, während Sie sich selbst in den Fuß schießen können, indem Sie einen Zeiger auf einen zufälligen Stapelplatz geben.Die Informatik-Antwort lautet:
In C wird der
[]
Operator anhand von Zeigern definiert, nicht anhand von Arrays. Insbesondere wird es in Bezug auf Zeigerarithmetik und Zeiger-Dereferenzierung definiert.In C ist ein Zeiger abstrakt ein Tupel
(start, length, offset)
mit der Bedingung, dass0 <= offset <= length
. Die Zeigerarithmetik ist im Wesentlichen eine aufgehobene Arithmetik für den Versatz, mit der Warnung, dass ein undefinierter Wert vorliegt, wenn das Ergebnis der Operation die Zeigerbedingung verletzt. Das Aufheben der Referenzierung eines Zeigers fügt eine zusätzliche Einschränkung hinzu, dieoffset < length
.C hat eine Vorstellung von
undefined behaviour
dem einen Compiler konkret darzustellen , das Tupel als eine einzelne Zahl erlaubt, und nicht jede Verletzung des Zeigers Zustand erkennen müssen. Jedes Programm, das die abstrakte Semantik erfüllt, ist mit der konkreten (verlustbehafteten) Semantik sicher. Alles, was gegen die abstrakte Semantik verstößt, kann vom Compiler kommentarlos akzeptiert werden und es kann alles tun, was es damit tun möchte.quelle
Arrays werden einfach als zusammenhängende Speicherbereiche angelegt. Ein Arrayzugriff wie a [i] wird in einen Zugriff auf den Speicherort addressOf (a) + i konvertiert . Damit der Code
a[-1]
vollkommen verständlich ist, bezieht er sich einfach auf die Adresse eins vor dem Start des Arrays.Das mag verrückt erscheinen, aber es gibt viele Gründe, warum dies zulässig ist:
a[-1]
gültig sind. Wenn ich beispielsweise weiß, dass diesa
nicht der Anfang des Arrays ist, sondern ein Zeiger in die Mitte des Arrays, wirda[-1]
einfach das Element des Arrays abgerufen, das sich links vom Zeiger befindet.quelle
a[-1]
macht durchaus Sinn für einige Fälle vona
, in diesem speziellen Fall ist es einfach illegal (aber nicht vom Compiler abgefangen)Wie die anderen Antworten erklären, ist dieses Verhalten in C undefiniert . Beachten Sie, dass C als "Assembler auf hoher Ebene" definiert wurde (und meistens verwendet wird). Die Benutzer von C schätzen es für seine kompromisslose Geschwindigkeit, und das Überprüfen von Dingen zur Laufzeit kommt (meistens) aus Gründen der Leistung nicht in Frage. Einige C-Konstrukte, die für Leute, die aus anderen Sprachen stammen, unsinnig aussehen, sind in C wie folgt durchaus sinnvoll
a[-1]
. Ja, es macht nicht immer Sinn (quelle
Mit einer solchen Funktion können Speicherzuweisungsmethoden geschrieben werden, die direkt auf den Speicher zugreifen. Eine solche Verwendung besteht darin, den vorherigen Speicherblock unter Verwendung eines negativen Array-Index zu überprüfen, um festzustellen, ob die beiden Blöcke zusammengeführt werden können. Ich habe diese Funktion bei der Entwicklung eines nichtflüchtigen Speichermanagers verwendet.
quelle
C ist nicht stark typisiert. Ein Standard-C-Compiler prüft keine Array-Grenzen. Die andere Sache ist, dass ein Array in C nichts anderes als ein zusammenhängender Speicherblock ist und die Indizierung bei 0 beginnt, so dass ein Index von -1 die Stelle ist, an der sich das vorhergehende Bitmuster befindet
a[0]
.Andere Sprachen nutzen negative Indizes auf nette Weise. Gibt in Python
a[-1]
das letzte Element,a[-2]
das vorletzte Element usw. zurück.quelle
int
, soa[-5]
und, allgemeiner,int i; ... a[i] = ...;
korrekt eingegeben haben . Indexfehler werden nur zur Laufzeit erkannt. Natürlich kann ein cleverer Compiler einige Verstöße erkennen.In einfachen Worten:
Alle Variablen (einschließlich Arrays) in C werden gespeichert. Angenommen, Sie haben 14 Byte "Speicher" und initialisieren Folgendes:
Berücksichtigen Sie außerdem die Größe eines Int als 2 Byte. Dann wird hypothetisch in den ersten 2 Bytes des Speichers die ganze Zahl a gespeichert. In den nächsten 2 Bytes wird die ganze Zahl der ersten Position des Arrays gespeichert (das bedeutet Array [0]).
Wenn Sie dann Array [-1] sagen, verweisen Sie auf die Ganzzahl, die im Speicher direkt vor Array [0] gespeichert ist. Dies ist in unserem Fall hypothetisch die Ganzzahl a. In Wirklichkeit werden Variablen nicht auf diese Weise im Speicher abgelegt.
quelle
quelle