2013-06-18 17 views
7

Ich lese den Abschnitt über Array-Arithmetik in K & R und stieß auf etwas neugierig. Ich habe den ganzen Absatz für den Kontext geschrieben, aber ich konzentriere mich hauptsächlich auf den fetten Teil.C Zeiger Arithmetik für Arrays

Wenn p und q Punkt an die Mitglieder des gleichen Array, dann Beziehungen wie ==, ! =, <,> =, usw., richtig zu arbeiten. Zum Beispiel ist p < q wahr, wenn p auf ein früheres Mitglied des Arrays zeigt als q. Jeder Zeiger kann sinnvoll für Gleichheit oder Ungleichheit mit Null verglichen werden. Aber das Verhalten ist nicht definiert für Arithmetik oder Vergleiche mit Zeigern, die nicht auf Mitglieder desselben Arrays zeigen. (Es gibt eine Ausnahme: die Adresse des ersten Elements über das Ende eines Arrays kann in Zeigerarithmetik verwendet werden.)

Was für diese Ausnahme ist der Grund? Wird ein zusätzliches Stück Speicher am Ende eines Arrays zugewiesen, wenn ihre Größe definiert ist? Wenn ja, zu welchem ​​Zweck? Soll das Array mit einem Nullzeichen enden?

+1

Der einfachste Weg dies zu verstehen ist: a [b] = a + b. Eine Erklärung finden Sie auf Seite 98 – SheetJS

+2

@Nirk: Huh? Was hat das damit zu tun? – jason

Antwort

8

Der Grund ist, so können Sie einen Zeiger in einer Schleife wie folgt erhöht:

char a[42], *p; 

for (p = a; p < &a[sizeof a]; p++) // or p != &a[sizeof a] 
{ 
    /* ... */ 
} 

Ohne die zusätzliche Regel, würde dies nicht definiertes Verhalten, weil der Zeiger ungültig wäre.

0

Am Ende des Arrays ist kein zusätzlicher Speicher reserviert. Es besagt nur, dass Sie die Adresse, die unten in der Zeigerarithmetik mit 'End' markiert ist. Begin zeigt auf das erste Element des Arrays. Ende zeigt auf das erste Element nach das Ende des Arrays.

----------------- 
| | | | | 
----------------- 
^    ^
Begin   End 
4

Ist ein zusätzlicher Teil des Speichers zum Ende jedes Array zugeordnet wird, wenn ihre Größe definiert ist,?

Nein. Der von Ihnen angegebene Kontext ist wichtig. Die Ausnahme, die Sie fett darstellen, bezieht sich auf Zeigerarithmetik (und Relationen). Es wird gesagt, dass wenn Sie Zeigerbeziehungen zwischen Zeigern, die nicht zeigen, auf Mitglieder des gleichen Array, dann erhalten Sie udb. Es gibt jedoch eine einzige Ausnahme, wenn einer der Zeiger auf das erste Element nach dem Ende des Arrays zeigt.

Wenn ja, zu welchem ​​Zweck?

null antworten, da es eine falsche Prämisse voraussetzt.

Soll das Array mit einem Nullzeichen enden?

No.

Der Grund dafür ist, so dass gegenüber dem Ende des Arrays legal ist, das heißt, wenn Vergleiche mit &a[sizeof a]a ein Array ist. Beachten Sie, dass &a[sizeof a] das erste Element nach dem Ende des Arrays ist.Wenn ein Zeiger auf ein Element von a oder auch das erste Element nach dem Ende des Arrays ist, kann mit &a[sizeof a] verglichen werden.

Ich zitiere aus der C99 specification, Abschnitt 6.5.8.5.

Wenn zwei Zeiger verglichen werden, hängt das Ergebnis von den relativen Positionen im Adressraum der Objekte ab, auf die verwiesen wird. Wenn zwei Zeiger auf Objekt oder unvollständige Typen beide auf dasselbe Objekt zeigen oder beide auf das letzte Element desselben Array-Objekts zeigen, vergleichen sie das Gleiche. Wenn die Objekte, auf die verwiesen wird, Mitglieder desselben Aggregatobjekts sind, vergleichen Zeiger auf später deklarierte Strukturelemente größer als Zeiger auf zuvor in der Struktur deklarierte Elemente, und Zeiger auf Arrayelemente mit größeren tiefgestellten Werten vergleichen größere als Zeiger auf Elemente desselben Arrays mit niedrigeren Indexwerten. Wenn der Ausdruck P auf ein Element eines Array-Objekts zeigt und der Ausdruck Q auf das letzte Element desselben Array-Objekts zeigt, wird der Zeigerausdruck größer als P verglichen. In allen anderen Fällen ist das Verhalten nicht definiert.

0

Sie dürfen nur die Adresse des Objekts am Ende des Arrays berechnen und es wird versprochen, dass Sie dabei keine Probleme bekommen. Sie dürfen diesen Zeiger nicht dereferenzieren. Ein Beispiel dafür, wo dieses Versprechen wichtig ist, ist, wo ein Objekt sonst am Ende des Speichers zugewiesen werden könnte, so dass die Adresse eines anderen Ende arithmetischen Überlauf verursachen würde, wenn Sie die Adresse berechnet haben. Wenn Sie einen Zeiger durch dieses Array durchlaufen würden, könnte nach der letzten Iteration der arithmetische Überlauf dazu führen, dass der Zeiger sich um NULL bewegt und auf ihn zeigt.

Dies könnte dazu führen, dass die Vergleichsergebnisse invertiert werden und alle Arten von Alarmglocken mit arraygebundenen Checkern auslösen könnten, oder es könnte einfach die falsche Adresse berechnen, wenn die CPU beispielsweise sättigende Arithmetik verwendet.

Es ist also die Pflicht des Compilers und Linkers sicherzustellen, dass dies nicht passieren kann, und es ist die Pflicht des Programmierers, sicherzustellen, dass Compiler und Linker ihre Verantwortung auf diesen einen einfachen Fall beschränkt haben und t müssen die gleiche Garantie aufrechterhalten, wenn Sie n Elemente weiter vom Ende laufen lassen.