Ich habe das Buch von K & R über C gelesen und festgestellt, dass die Zeigerarithmetik in C den Zugriff auf ein Element über das Ende eines Arrays hinaus ermöglicht. Ich weiß, dass man in C fast alles mit dem Speicher machen kann, aber ich verstehe einfach nicht, was der Zweck dieser Besonderheit ist.
Antworten
Zu viele Anzeigen?C erlaubt nicht Zugang in den Speicher über das Ende des Arrays hinaus. Es erlaubt jedoch, dass ein Zeiger auf ein Element jenseits des Endes des Arrays zeigt. Diese Unterscheidung ist wichtig.
Das ist also in Ordnung:
char array[N];
char *p;
char *end;
for (p = array, end = array + N; p < end; ++p)
do_something(p);
(Tun *end
wäre ein Fehler).
Und das zeigt den Grund, warum diese Funktion nützlich ist: ein Zeiger, der auf das (nicht vorhandene) Element nach dem Ende des Arrays zeigt, ist nützlich für Vergleiche, z. B. in Schleifen.
Technisch gesehen ist das alles, was der C-Standard erlaubt. In der Praxis prüft die C-Implementierung (Compiler und Laufzeit) jedoch nicht, ob Sie über das Ende des Arrays hinaus auf Speicher zugreifen, unabhängig davon, ob es sich um ein oder mehrere Elemente handelt. Es müsste eine Überprüfung der Grenzen stattfinden, und das würde die Programmausführung verlangsamen. Die Arten von Programmen, für die C am besten geeignet ist (Systemprogrammierung, allgemeine Bibliotheken), profitieren in der Regel mehr von der Geschwindigkeit als von der Sicherheit, die eine Überprüfung der Grenzen bieten würde.
Das bedeutet, dass C vielleicht kein gutes Werkzeug für die Programmierung von Allzweckanwendungen ist.
Oft ist es nützlich, die "Endposition" anzugeben, die eine Position nach der eigentlichen Zuweisung liegt, so dass Sie Code wie diesen schreiben können:
char * end = begin + size;
for (char * curr = begin; curr < /* or != */ end ; ++curr) {
/* do something in the loop */
}
Der C-Standard sagt ausdrücklich, dass dieses Element eine gültige Speicheradresse ist, aber eine Dereferenzierung wäre trotzdem keine gute Idee.
Warum hat es diese Garantie? Nehmen wir an, Sie hätten einen Rechner mit 2^16 Byte Speicher, Adressen 0000-FFFF, 16-Bit-Zeigern. Angenommen, Sie erstellen ein 16-Byte-Array. Könnte der Speicher bei FFF0 zugewiesen werden?
Es sind 16 zusammenhängende Bytes frei, aber:
begin + size == FFF0 + 10 (16 in hex) == 10000
der aufgrund der Zeigergröße auf 0000 umbricht. Jetzt die Schleifenbedingung:
curr < end == FFF0 < 0000 == false
Anstatt über das Array zu iterieren, würde die Schleife nichts tun. Dies würde eine Menge Code kaputt machen, daher sagt der C-Standard, dass die Zuweisung nicht zulässig ist.
Wenn Sie über den zugewiesenen Speicher hinaus lesen oder schreiben, dann sagt der C-Standard "undefiniertes Verhalten". Das bedeutet, dass so ziemlich alles passieren kann, vielleicht jetzt, vielleicht in einer Woche, vielleicht in 5 Jahren oder vielleicht nie und man kommt damit durch.
Mein Chef hatte ein paar Sprüche: "Es gibt kein korrektes C-Programm, nur eines, das noch nicht schiefgegangen ist" "Das einzig Vernünftige, was man über Speicherkorruption sagen kann, ist nichts."
Er hatte immer Recht.