2013-07-15 6 views
8

Dieses Codebeispiel gibt das Array korrekt aus.Inkonsistenz bei der direkten Verwendung des Zeigers zu einem Array und der Adresse eines Arrays

int b[2] = {1, 2}; 
int *c = &b; 
int i, j,k = 0; 
for (i = 0;i < 2; i++) { 
    printf("%d ", *(c+i)); 
} 

während dieser druckt zwei Müllwerte.

int b[2] = {1, 2}; 
int i, j,k = 0; 
for (i = 0;i < 2; i++) { 
    printf("%d ", *(&b+i)); 
} 

Warum verhalten sich die beiden Codebeispiele anders?

+0

Wofür wird 'k' verwendet? – 0x499602D2

+0

zweiten Code - [undefined Verhalten] (http://en.wikipedia.org/wiki/Undefined_behavior) so verhält sich anders –

+2

+1 nette Frage, aber leicht beantwortbar, immer noch nützlich :) – 0decimal0

Antwort

1
int *c = &b;  

Dies ist eigentlich nicht gültig. Du brauchst eine Besetzung.

int *c = (int *) &b; 

Die beiden Ausdrücke:

*(c+i) 

and 

*(&b+i) 

sind nicht gleich. In dem ersten Ausdruck wird i zu einem int * hinzugefügt und in dem zweiten Ausdruck wird i zu einem int (*)[2] hinzugefügt. Mit int *c = (int *) &b; konvertieren Sie eine int (*)[2] in eine int *. c + i verweist auf das i -th int Element von c aber &b+i verweist auf das int [2] Element von b Verschieben des Zeigerwerts außerhalb des tatsächlichen Array-Objekts.

+2

Es stimmt, dass 'int * c = & b;' nicht gültig ist, aber das Hinzufügen einer Besetzung ist nicht die Lösung. Wenn Sie die Adresse des ersten Elements haben wollen, schreiben Sie 'int * c = b;' oder 'int * c = &b[0];'.Wenn Sie die Adresse des Arrays wollen, schreiben Sie 'int (* c) [2] = & b;'. Das Konvertieren eines Zeigers von einem Typ in einen anderen (inkompatiblen) Typ ist selten die Antwort - es sei denn, Sie stellen eine ziemlich merkwürdige Frage. –

+0

@KeithThompson Ich lese @ Andreys Antwort, bezweifle, dass "Ist es möglich, dass mit einigen Architektur/Compiler' int * c = & b; 'geben undefiniertes Verhalten **? ** Kann ich Warnung vom Compiler generiert, wie ich Wert weiß- weise sind "b" und "b" gleich (semantisch sind beide verschieden). –

+1

@GrijeshChauhan: Da 'b' vom Typ' arr [2] 'ist, ist' int * c = & b; 'eine Einschränkungsverletzung. Ein konformer Compiler * muss * ihn diagnostizieren und * kann * ihn ablehnen.Wenn er ihn ablehnt, ist sein Verhalten nicht nur undefiniert, er hat auch kein Verhalten. Tun Sie das nicht. –

5

dies wegen der Zeigertyp ist, auf die der Zeiger arithmetische Operation ptr+i angewendet wird:

  • Im ersten Fall, fügen Sie i auf einen Zeiger auf int, was dasselbe ist ein Array als Indizierung. Da der Zeiger auf ein Array der gleiche wie der Zeiger auf sein erstes Element ist, funktioniert der Code.
  • Im zweiten Fall fügen Sie i zu einem Zeiger auf ein Array von zwei int s hinzu. Daher werden Sie durch den Zusatz über den zugewiesenen Speicher hinaus versetzt, was zu undefiniertem Verhalten führt. Hier

ist eine kurze Darstellung dieses Punktes:

int b[2] = {1,2}; 
printf("%p\n%p\n%p\n", (void*)&b, (void*)(&b+1), (void*)(&b+2)); 

Auf einem System mit 32-Bit-int s diesen Adressen von acht Bytes getrennt druckt - die Größe int[2]:

0xbfbd2e58 
0xbfbd2e60 
0xbfbd2e68 
+0

Richtig, bis auf die Frage :) A +1 Gut verdiente – 0decimal0

9

Die Deklaration:

int b[2] = {1, 2}; 

Erstellt ein Array von zwei int mit den Werten 1, 2.
in einer Systemgröße von int Angenommen 4-Byte, dann wird das Array b[] im Speicher etwas gespeichert werden soll, wie die folgende:

first ele  +----------+     
    (b + 0) ---►|  1 | 0xbf5c787c <----- &b , (c + 0) 
next ele   +----------+ 
    (b + 1) ---►|  2 | 0xbf5c7880 <------------- (c + 1) 
       +----------+    
    (b + 2) ---►|  ? | 0xbf5c7884 <----- (&b + 1) next array 
       +----------+      
      ---►|  ? | 0xbf5c7888 
       +----------+ 
      ---►|  ? | 0xbf5c788c <----- (&b + 2) next array 
       +----------+  
      ---►|  ? | 0xbf5c7890 
       +----------+    

? means garbage value 
b[] array in memory from 0xbf5c787c to 0xbf5c7880 
each cell is four bytes 

In obigen Diagramm Speicherzellen mit dem Wert ? garbage Werte bedeuten und nicht zugewiesen (Speicher von 0xbf5c7884 ist nicht für unser Array reserviert).Die Werte 1, 2 werden im Speicher unter der Adresse 0xbf5c787c und 0xbf5c7880 gespeichert, die im Array b[] zugewiesen sind.

Lassen Sie uns anstelle von Druckwerten, drucken wir Adressen des Speichers, die Sie in Ihrem Code Zugriff mit (c + i) und (&b + i) für diese betrachten folgendes Programm:

#include<stdio.h> 
int main(){ 
    int b[2] = {1, 2}; 
    int i = 0; 
    int *c = &b; //Give warning: "assignment from incompatible pointer type" 
    printf("\n C address: "); // outputs correct values 
    for (i = 0; i < 2; i++) { 
    printf("%p ", (void*)(c + i)); 
    } 
    printf("\n B address: "); // outputs incorrect values/ and behaving differently 
    for (i = 0; i < 2; i++) { 
    printf("%p ", (void*)(&b + i)); // Undefined behavior 
    } 
    return 1; 
} 

Ausgänge:

C address: 0xbf5c787c 0xbf5c7880 
B address: 0xbf5c787c 0xbf5c7884 

prüfen diese Code funktioniert @Codepade
Beachten Sie, (c + i) druckt korrekte Adresse der Zellen mit Wert 1,Daher sind die Ausgaben in Ihrem ersten Code korrekt. Während (&b + i) einen Adresswert ausgibt, der nicht dem Array b[] zugeordnet ist (also außerhalb des Arrays b[]) und der Zugriff auf diesen Speicher zur Laufzeit ein nicht definiertes Verhalten (nicht vorhersagbar) ergibt.

Tatsächlich gibt es einen Unterschied zwischen b und &b.

  • b ist ein Array und seine Art ist int[2], b in int* als Adresse in den meisten Ausdrücke für erstes Element abklingt (lies: some exceptions where array name not decaying into a pointer to first element?). Und b + 1 zeigt auf nächste int Elemente in Array (beachten Sie das Diagramm).

  • &b ist Adresse kompletten Array und seine Art ist int(*)[2], (&b + 1) Punkte zum nächsten Array vom Typ int[2], die nicht in Ihrem Programm (Bekanntmachung im Diagramm, dass in dem (&b + 1) Punkten) zugeordnet ist.

Um einige andere interessante Unterschiede zwischen b und &b wissen lassen: What does sizeof(&array) return?

Im ersten Code Schnepfe, wenn Sie c = &b tun, Sie Adresse des Array int* zuweisen (in unserem Beispiel 0xbf5c787c). Mit GCC-Compiler wird diese Anweisung warnen: "Zuweisung von inkompatiblen Zeigertyp".
Da c Zeiger auf int ist, wird *(c + i) gedruckt Integer bei Adresse (c + i) gespeichert. Für i = 1 zeigt der Wert (c + 1) auf das zweite Element im Array (in unserem Beispiel 0xbf5c7880) daher *(c + 1) druckt 2 korrekt.

Bezüglich der Zuweisung int *c = &b; im ersten Code empfehle ich sehr lesen @ AndreyT answer unten. Die korrekte und einfache Art und Weise Array-Elemente zuzugreifen Zeiger verwenden, wird wie folgt aussehen:

int b[2] = {1, 2}; 
int *c = b; // removed &, `c` is pointer to int 
int i; 
for (i = 0; i < 2; i++){ 
    printf("%d ", *(c + i)); 
// printf("%d ", c[i]); // is also correct statement 
} 

In Ihrem zweiten Code, das Hinzufügen i-&b es Ihnen Speicher * Dereferenzierungsoperator mit Zugriff außerhalb zugewiesenen Speicher und in printf Anweisung zeigt machen Ursache ungültiger Speicherzugriff und Verhalten dieses Codes zur Laufzeit ist Undefined. Das ist der Grund dafür, dass sich der zweite Code bei unterschiedlicher Ausführung anders verhält.

Ihr Code kompiliert, weil syntaktisch es richtig ist, aber zur Laufzeit kann der Zugriff auf nicht zugeordneten Speicher vom OS-Kernel erkannt werden. Dies kann dazu führen, dass der OS-Kernel einen Signal-Core-Dump an den Prozess sendet, der die Exception verursacht hat (interessant zu beachten: wenn das Betriebssystem einen Speicherrechtsverletzung durch einen Prozess erkennt) - Ein ungültiger Zugriff auf gültigen Speicher ergibt: SIGSEGV und Zugriff auf eine ungültige Adresse : SIGBUS). Im Wertfall kann Ihr Programm ohne Fehler ausgeführt werden und Müll-Ergebnisse erzeugen.

Bezüglich zweiten Code, korrekte Art und Weise Array 'Zeiger auf Array' zu drucken wie weiter unten sein:

#include<stdio.h> 
int main(){ 
    int b[2] = {1, 2}; 
    int i; 
    int (*c)[2] = &b; // `c` is pointer to int array of size 2 
    for(i = 0; i < 2; i++){ 
    printf(" b[%d] = (*c)[%d] = %d\n", i, i, (*c)[i]); // notice (*c)[i] 
    } 
    return 1; 
} 

Ausgang:

b[0] = (*c)[0] = 1 
b[1] = (*c)[1] = 2 

prüfen @codepade. Point to be notice Klammern um *c wird als Priorität von [] Operator ist dann höher als * Dereferenz Operator (während, wenn Sie Zeiger auf int verwenden Sie keine Klammern wie in oben genannten Code) benötigt.

+1

+1 für die Diagrammbeschreibung :) – 0decimal0

+0

"Diese Anweisung sollte Ihnen Warnung für Typ Mismatch Zuweisung geben." - Nicht unbedingt, ein Zeiger auf Array konvertiert implizit in einen Zeiger auf das erste Element. IMHO ist dies ein entscheidender Detail: – Potatoswatter

+0

@Potatoswatter Aber ich habe gerade mit GCC gecheckt ich habe: 'Zuweisung von inkompatiblen Zeigertyp'. Soll ich das entfernen ?? –

1

Der erste Code ist kaputt. Die Zuordnung

int *c = &b; 

ist ungültig. Die rechte Seite hat den Typ int (*)[2], während das Objekt auf der linken Seite den Typ int * hat. Dies sind verschiedene, inkompatible Typen. Der Compiler hätte Sie über diesen Fehler informieren sollen, indem Sie eine Diagnosemeldung ausgegeben haben. Ignorieren Sie die von Compilern ausgegebenen Diagnosemeldungen nicht.

Der Code wurde trotz des oben genannten Problems vom Compiler aufgrund einer nicht standardmäßigen Compiler-Erweiterung akzeptiert, die es dem Compiler ermöglichte, int (*)[2] Zeiger in int * Typ zu konvertieren, der den numerischen Wert des Zeigers (die physische Adresse) beibehielt). Also, Sie haben am Ende mit int * Zeiger auf den Anfang Ihrer int [2] Array. Es ist nicht überraschend, dass der Zugriff auf den Speicher über diesen Zeiger den Inhalt des Arrays anzeigt.

Ihr zweiter Code ist auch auf mehrere Arten aufgeteilt. Es leidet nicht unter dem ersten Problem, da Sie keine Umwandlung von &b Wert erzwingen (der wiederum den Typ hat), aber Zeigerarithmetik direkt auf &b anwenden. Gemäß den Regeln der Zeigerarithmetik erzeugt der Ausdruck &b + 1 einen Zeiger, der über das ursprüngliche Array b hinausweist. Die Dereferenzierung eines solchen Zeigers ist illegal. So erzeugt *(&b + 1) bereits undefiniertes Verhalten. Darüber hinaus hat der Ausdruck *(&b + 1) den Typ int [2], der in den Zeigertyp int * zerfällt. Also, in Ihrem zweiten Code versuchen Sie, einen int * Wert mit %d Format-Spezifizierer zu drucken. Dies ist auch undefiniertes Verhalten. Die Manifestationen dieses undefinierten Verhaltens sehen Sie in Ihrem zweiten Beispiel.

Mit anderen Worten, im ersten Stück Code haben Sie mehr Glück als in der zweiten, weshalb die Ausgabe der ersten aussagekräftiger aussieht.

+0

Andrey Ist es möglich, dass mit einigen Architektur/Compiler 'int * c = & b;' geben undefiniertes Verhalten **? ** Wie ich Wert weiß weise beide '& b' und' b' sind gleich, aber semantisch beide sind verschieden, also ich Fühle einen Code wegen 'int * c = & b;' kompiliert mit Warnung aber ich kann diese Warnung ignorieren. Ist meine Annahme richtig? –

Verwandte Themen