In den folgenden zwei Zeilen
%Vor%Dies druckt 5 auf dem Bildschirm. Wenn Sie anstelle von & amp; a verwenden,
%Vor%Dies druckt 1
Sowohl a als auch & amp; a sind die Anfangsadresse des Arrays. Warum ist dieser Unterschied?
Auch char * ptr = & amp; a + 1;
zeigt eine Warnung.
Da du ihm völlig neu zu sein scheinst, lass es mich dir einfach erklären, anstatt für die rigorose Erklärung zu gehen.
Sie sehen, für Ihr Programm oben haben a
und &a
denselben numerischen Wert , und ich glaube, da liegt Ihre ganze Verwirrung. Sie mögen sich fragen, ob sie das sind Gleiches, das folgende sollte die nächste Adresse nach a
in beiden Fällen geben, die durch Zeigerarithmetik geht:
Aber es ist nicht so! Basisadresse eines Arrays ( a
hier) und Adresse eines Arrays sind nicht gleich! a
und &a
können numerisch gleich sein, aber sie sind nicht derselbe Typ . a
ist vom Typ char*
, während &a
vom Typ char (*)[5]
ist, dh &a
ist ein Zeiger auf (Adresse von) und ein Array der Größe 5.But a
, wie Sie wissen, ist Adresse des ersten Elements des Arrays .Nummern entsprechen sie der Abbildung mit ^ .
Aber wenn Sie diese zwei Zeiger / Adressen inkrementieren, dh als (a+1)
und (&a+1)
, ist die Arithmetik völlig anders. Während im ersten Fall "springt" sie auf die Adresse des nächsten Elements im Array, in Im letzteren Fall springt um 5 Elemente, denn das ist die Größe eines Arrays aus 5 Elementen! .
Das Folgende wird einen Fehler bezüglich der nicht spezifizierten Grenze für das Feld ergeben, da die Größe nicht explizit wie unten angegeben bedeutet, dass das Programm nicht weiß, wie viele Elemente "springt", wenn etwas (& amp; a + 1) angetroffen wird.
%Vor% Nun zu dem Teil, wo Sie die Zeiger / Adressen als (ptr-1)
dekrementieren. Im ersten Fall sollten Sie wissen, was in der Anweisung darüber passiert, wo sie umgewandelt wird, um% co_de einzugeben %:
Was passiert, ist, dass Sie das ursprüngliche char*
von type
, das vom Typ (&a+1)
war, "abzustreifen" und es jetzt auf den Typ char (*)[5]
werfen, was dasselbe ist wie das von char*
, dh die Basisadresse des Arrays. (Beachten Sie erneut den Unterschied zwischen Basisadresse eines Arrays und Adresse eines Arrays . So nach der Besetzung und Zuweisung in der obigen Anweisung, gefolgt Durch das Dekrement in a
, printf()
wird nun der Speicherplatz direkt nach dem letzten Element des Arrays angegeben, also ptr
.
Wenn Sie also den Zeiger 5
nach dem Dekrementieren als ptr
dereferenzieren, wird der Wert von *(ptr-1)
wie erwartet ausgegeben.
Vergleichen Sie es nun mit dem zweiten Fall, in dem 5
gedruckt ist. Sehen Sie sich die Illustration an, die ich mit dem Symbol ^ angegeben habe. Wenn Sie 1
als a
inkrementiert haben, zeigt es auf das zweite Element des Arrays, dh a+1
, und Sie haben diese Adresse 2
.So zugewiesen, wenn Sie ptr
it als ptr
dekrementieren, Es springt ein Element zurück und zeigt nun auf das erste Element des Arrays, dh (ptr-1)
.Dereferenzierung 1
im zweiten Fall ergibt ptr
.
Ich hoffe, das hat alles klar gemacht.
Arrays sind keine Zeiger! Weitere Informationen finden Sie in Abschnitt 6 der FAQ zu comp.lang.c .
Sehen wir uns zuerst Ihren zweiten Fall an, da er eher "normal" und idiomatisch aussieht. Zeile für Zeile:
a
mit 5 char
elements. a
) zerfällt in diesem Kontext in einen Zeiger auf sein erstes Element. Sie fügen 1
hinzu und weisen das Ergebnis ptr
zu. ptr
zeigt auf 2
. Keine Besetzung ist notwendig, obwohl du eine hast. 1
von ptr
und dann dereferenzieren und drucken - daher erhalten Sie die 1
. Lassen Sie uns nun den ersten Fall, Zeile für Zeile, erneut ansprechen:
a
mit 5 char
elements. a
, was einen Zeiger vom Typ char (*)[5]
ergibt. Sie fügen dann 1
zu diesem Zeiger hinzu - wegen der Zeigerarithmetik überschreitet dieser neue Zeiger das Byte direkt nach 5
im Speicher. Dann gibst du typcast ein (diesmal erforderlich) und weist diesen Wert ptr
zu. 1
von ptr
und referenzieren und drucken dann. ptr
ist ein char *
, also bewegt diese Subtraktion den Zeiger einfach um eins von "eins nach dem Ende von a
" zurück, um auf das letzte Element von a
zu zeigen. Daher erhalten Sie die 5
. Schließlich gibt der Grund für char *ptr=&a+1;
eine Warnung, weil C Konvertierungen zwischen Zeigertypen erfordert, um eine explizite Umwandlung zu haben. Wie oben erwähnt, ist &a
vom Typ char (*)[5]
, nicht char *
. Um diesen Wert also einer char *
-Variablen zuzuweisen, benötigen Sie die explizite Umwandlung.
Der Unterschied besteht in dem Typ des Zeigers, den Sie erhalten:
a
selbst repräsentiert einen Zeiger auf das ursprüngliche Element des Arrays. Wenn auf diese Weise interpretiert, z.B. In einem Ausdruck a+1
wird davon ausgegangen, dass der Zeiger auf ein einzelnes Zeichen zeigt. &a
verwenden, zeigt der Zeiger auf ein Array von fünf Zeichen. Wenn Sie einem Zeiger eine Ganzzahl hinzufügen, wird die Anzahl der Bytes, um die der Zeiger verschoben wird, durch den Typ des Objektzeigers durch den Zeiger bestimmt. Falls der Zeiger auf char
zeigt, wird durch Hinzufügen von N
der Zeiger um N
bytes vorgerückt. Wenn der Zeiger auf ein Array von fünf char
s zeigt, wird durch Hinzufügen von N
der Zeiger um 5*N
bytes vorgerückt.
Das ist genau der Unterschied, den Sie bekommen: Ihr erstes Beispiel rückt den Zeiger auf das Element nach dem Ende des Arrays (was legal ist) vor und verschiebt es dann zurück zum letzten Element. Ihr zweites Beispiel hingegen verschiebt den Zeiger zum zweiten Element und verschiebt es dann zurück, um auf das Anfangselement des Arrays zu zeigen.
In was Sie hineinlaufen, ist eine Subtilität der Zeigerarithmetik.
Der Compiler behandelt "a" als einen Zeiger auf char - eine Entität, die 1 Byte groß ist. Hinzufügen von 1 zu diesem ergibt einen Zeiger, der um die Größe der Entität erhöht wird (d.h. 1).
Der Compiler behandelt "& amp; a" als einen Zeiger auf ein Array von Zeichen - eine Entität, die 5 Bytes groß ist. Hinzufügen von 1 zu diesem ergibt einen Zeiger, der um die Größe der Entität erhöht wird (d. H. 5).
So funktioniert die Pointer-Arithmetik. Wenn Sie einen Zeiger zu einem Zeiger hinzufügen, wird er um die Größe des Typs erhöht, auf den er zeigt.
Die lustige Sache ist natürlich, dass, wenn es darum geht, den Wert von "a" oder "& amp; a" zu bewerten, beide beim Dereferenzieren zu derselben Adresse ausgewertet werden. Deshalb sehen Sie die Werte, die Sie tun.
Arrays "verfallen" in Zeiger auf das erste Element. Wenn Sie also die Adresse von a eingeben, erhalten Sie einen Zeiger auf ein Array von 5 Zeichen, was dem Deklarieren von char[][5]
entspricht. Und wenn dieser Zeiger inkrementiert wird, geht er zum nächsten Element des Arrays char[][5]
über - das sind jeweils 5 Zeichen. Dies unterscheidet sich von der Erhöhung des Zeigers, der vom char[5]
-Array abfällt - also jeweils ein Zeichen.