C / C ++: Zeigerarithmetik

8

Ich habe ein wenig in Pointer Arithmetic gelesen und bin auf zwei Dinge gestoßen, die ich nicht verstehen konnte und auch nicht weiß, dass es

ist %Vor%

und auch

%Vor%

Kann mir bitte jemand sie erklären, wie funktionieren sie und wann werden sie benutzt?

Bearbeiten:

Was ich sagen wollte, ist was sie produzieren, wenn ich nur zwei Adressen nehme und sie subtrahiere

Und wenn ich zwei Adressen nehme und sie vergleiche, was ist das Ergebnis oder Vergleich basierend auf

Bearbeiten: Ich verstehe jetzt das Ergebnis der Subtraktion von Adressen, aber Adressen zu vergleichen, bekomme ich immer noch nicht.

Ich verstehe, dass 1 & lt; 2, aber wie ist eine Adresse größer als eine andere und was sind sie auf

verglichen     
Mohamed Ahmed Nabil 29.07.2012, 23:47
quelle

6 Antworten

16

Die Pointer-Subtraktion ergibt die Anzahl der Array-Elemente zwischen zwei Zeigern desselben Typs.

Zum Beispiel

%Vor%

Zeigervergleich. Zum Beispiel für den Operator > : Die Operation > ergibt 1 , wenn das spitz zulaufende Arrayelement oder Strukturelement auf der linken Seite hinter dem spitz zulaufenden Arrayelement oder Strukturelement auf der rechten Seite liegt und es nachgibt 0 ansonsten. Denken Sie daran, Arrays und Strukturen sind geordnete Sequenzen.

%Vor%     
ouah 30.07.2012, 00:12
quelle
19

Mehrere Antworten hier haben angegeben, dass Zeiger Zahlen sind. Dies ist keine genaue Beschreibung von Zeigern, wie durch den C-Standard spezifiziert.

Zu einem großen Teil können Sie sich Zeiger als Zahlen und als Adressen im Speicher vorstellen, vorausgesetzt (a) Sie verstehen, dass die Zeiger-Subtraktion die Differenz von Bytes in Elemente (vom Typ der zu subtrahierenden Zeiger) konvertiert und b) Sie verstehen die Grenzen, in denen dieses Modell bricht.

Im Folgenden wird der C-Standard von 1999 (ISO / IEC 9899, ​​Zweite Ausgabe, 1999-12-01) verwendet. Ich erwarte, dass das Folgende ausführlicher ist, als der Fragesteller verlangt hat, aber angesichts einiger falscher Angaben hier, denke ich, dass genaue und genaue Informationen gegeben werden sollten.

Nach 6.5.6 Absatz 9 können Sie zwei Zeiger subtrahieren, die auf Elemente desselben Arrays oder auf einen nach dem letzten Element des Arrays zeigen. Wenn Sie also int a[8], b[4]; haben, können Sie einen Zeiger von einem Zeiger auf [2] auf einen [5] subtrahieren, da a [5] und a [2] Elemente im selben Array sind. Sie können auch einen Zeiger von einem Zeiger auf [8] auf einen [5] subtrahieren, da a [8] hinter dem letzten Element des Arrays liegt. (a [8] ist nicht im Array; a [7] ist das letzte Element.) Sie dürfen keinen Zeiger von einem Zeiger auf b [2] auf [5] subtrahieren, da a [5] nicht in der gleiche Anordnung wie b [2]. Genauer gesagt, wenn Sie eine solche Subtraktion durchführen, ist das Verhalten nicht definiert. Beachten Sie, dass nicht nur das Ergebnis unspezifiziert ist. Sie können nicht erwarten, dass Sie als Ergebnis eine möglicherweise unsinnige Zahl erhalten: Das Verhalten ist nicht definiert. Nach dem C-Standard bedeutet dies, dass der C-Standard nichts darüber aussagt, was als Konsequenz auftritt. Ihr Programm könnte Ihnen eine vernünftige Antwort geben, oder es könnte abbrechen, oder es könnte Dateien löschen, und all diese Konsequenzen würden in Übereinstimmung mit dem C-Standard sein.

Wenn Sie eine zulässige Subtraktion durchführen, ist das Ergebnis die Anzahl der Elemente vom zweiten spitzen Element zum ersten spitzen Element. Daher ist a[5]-a[2] 3 und a[2]-a[5] ist -3. Dies gilt unabhängig vom Typ a . Die C-Implementierung wird benötigt, um den Abstand von Bytes (oder von den verwendeten Einheiten) in Elemente des entsprechenden Typs zu konvertieren. Wenn a ein Array aus zwei oder acht Bytes ist, dann ist a[5]-a[2] 3 für 3 Elemente. Wenn a ein Array von Zeichen mit jeweils einem Byte ist, dann ist a[5]-a[2] 3 für 3 Elemente.

Warum sollten Zeiger niemals nur Zahlen sein? Auf einigen Computern, insbesondere älteren Computern, war die Adressierung von Speicher komplizierter. Frühe Computer hatten kleine Adressräume. Wenn die Hersteller größere Adressräume erstellen wollten, wollten sie auch eine gewisse Kompatibilität mit alter Software beibehalten. Sie mussten auch verschiedene Schemata zum Adressieren von Speicher aufgrund von Hardwarebeschränkungen implementieren, und diese Schemas könnten das Verschieben von Daten zwischen Speicher und Platte oder das Ändern spezieller Register in dem Prozessor, die kontrollierten, wie Adressen in physikalische Speicherorte konvertiert wurden, beinhalten. Damit Zeiger auf solchen Maschinen funktionieren können, müssen sie mehr Informationen enthalten als nur eine einfache Adresse. Aus diesem Grund definiert der C-Standard Pointer nicht nur als Adressen, sondern lässt sie auch arithmetisch mit ihnen arbeiten. Nur eine vernünftige Menge an Arithmetik ist definiert, und die C-Implementierung wird benötigt, um die notwendigen Operationen zur Verfügung zu stellen, damit diese Arithmetik funktioniert, aber nicht mehr.

Selbst auf modernen Maschinen kann es zu Komplikationen kommen. Bei Alpha-Prozessoren von Digital enthält ein Zeiger auf eine Funktion nicht die Adresse der Funktion. Es ist die Adresse eines Deskriptors der Funktion. Dieser Deskriptor enthält die Adresse der Funktion und enthält einige zusätzliche Informationen, die notwendig sind, um die Funktion korrekt aufzurufen.

Bezüglich relationaler Operatoren wie > sagt der C-Standard in 6.5.8 Absatz 5, dass Sie die gleichen Zeiger, die Sie subtrahieren können, wie oben beschrieben, vergleichen können, und Sie können auch Zeiger mit Mitgliedern vergleichen eines Aggregatobjekts (eine Struktur oder Union). Zeiger auf Elemente eines Arrays (oder seine Endadresse) werden in der erwarteten Weise verglichen: Zeiger auf höher indizierte Elemente sind größer als Zeiger auf niedriger indizierte Elemente. Zeiger auf zwei Mitglieder derselben Gewerkschaft sind gleichwertig. Für Zeiger auf zwei Elemente einer Struktur ist der Zeiger auf das später deklarierte Element größer als der Zeiger auf das zuvor deklarierte Element.

Solange Sie innerhalb der obigen Einschränkungen bleiben, können Sie sich Zeiger als Zahlen vorstellen, die Speicheradressen sind.

Normalerweise ist es für eine C-Implementierung einfach, das vom C-Standard geforderte Verhalten bereitzustellen. Selbst wenn ein Computer ein zusammengesetztes Zeigerschema hat, wie beispielsweise eine Basisadresse und ein Offset, werden normalerweise alle Elemente eines Arrays die gleiche Basisadresse verwenden, und alle Elemente einer Struktur werden die gleiche Basisadresse verwenden.So kann der Compiler einfach die Offset-Teile des Zeigers subtrahieren oder vergleichen, um die gewünschte Differenz oder den gewünschten Vergleich zu erhalten.

Wenn Sie jedoch Zeiger auf verschiedene Arrays auf einem solchen Computer subtrahieren, können Sie seltsame Ergebnisse erzielen. Es ist möglich, dass das durch eine Basisadresse und einen Versatz gebildete Bitmuster größer erscheint (wenn es als eine einzelne ganze Zahl interpretiert wird) als ein anderer Zeiger, obwohl es auf eine niedrigere Adresse im Speicher zeigt. Dies ist ein Grund, warum Sie sich an die Regeln des C-Standards halten müssen.

    
Eric Postpischil 30.07.2012 01:02
quelle
4

Das Subtrahieren von zwei Zeigeradressen gibt die Anzahl der Elemente dieses Typs zurück.

Wenn Sie also ein Array mit ganzen Zahlen und zwei Zeigern haben, wird durch das Subtrahieren dieser Zeiger die Anzahl der int-Werte zwischen, nicht der Anzahl der Bytes zurückgegeben. Gleiches mit Char-Typen. Sie müssen also vorsichtig damit umgehen, besonders wenn Sie mit einem Byte-Puffer oder breiten Zeichen arbeiten, dass Ihr Ausdruck den richtigen Wert berechnet. Wenn Sie Byte-basierte Puffer-Offsets für etwas benötigen, das kein einzelnes Byte für den Speicher verwendet (int, short, usw.), müssen Sie Ihre Zeiger zuerst auf char * umwandeln.

    
paddy 30.07.2012 00:04
quelle
-1

Zeiger können oft nur als Zahlen betrachtet werden, die die Speicheradresse darstellen, wie 0x0A31FCF20 (oder 2736770848 im Dezimalformat) oder 0xCAFEDEAD (manchmal verwenden Systeme dies, um einen Fehler anzuzeigen, ich erinnere mich nicht an die Details.)

Der Zeigervergleich wird oft beim Sortieren von Zeigerfeldern verwendet. Sortierte Arrays von Zeigern sind hilfreich, wenn Sie überprüfen müssen, ob sich ein Zeiger in einer Liste von Zeigern befindet. Wenn die Liste sortiert ist, müssen Sie nicht jedes Element der Liste durchsehen, um herauszufinden, ob sich der Zeiger in dieser Liste befindet. Sie müssen Vergleiche verwenden, um eine Liste zu sortieren.

Zeigerarithmetik wird häufig verwendet, wenn Sie einen Zeiger auf einen Datenblock haben und auf etwas zugreifen müssen, das nicht am Anfang des Datenblocks steht. Zum Beispiel:

%Vor%

Dies würde ausgeben:

%Vor%

Hier haben wir die Zeichenfolge nach den ersten 6 Zeichen von "Hallo Welt!" oder "world!" . Beachten Sie, dass Sie stattdessen std::string verwenden sollten, wo es verfügbar ist, wenn möglich. Ein der Zeigerarithmetik sehr ähnliches Konzept sind Random-Access-Iteratoren.

Das Subtrahieren von Zeigern kann Ihnen helfen, den Abstand zwischen diesen beiden Zeigern zu finden. Wenn Sie einen Zeiger auf das erste Element eines Arrays und einen Zeiger auf ein Element nach dem letzten Element des Arrays haben, hilft Ihnen das Subtrahieren dieser beiden Zeiger, die Größe des Arrays zu finden.

Ein weiterer Fall, in dem Sie Zeiger als Ganzzahlen behandeln könnten, ist eine optimierte Version einer verknüpften Liste, die als XOR-verknüpfte Liste bezeichnet wird. Mehr Details dazu finden Sie hier . Ich kann darüber hinausgehen, wenn Sie möchten; lass es mich in den Kommentaren wissen.

    
vedosity 30.07.2012 00:09
quelle
-1

Der erste Ausdruck subtrahiert einen Zeiger von einem anderen. Als ein einfaches Beispiel, warum dies nützlich sein könnte, betrachten Sie eine C-Zeichenfolge. Die Zeichenfolge befindet sich in zusammenhängendem Speicher. Wenn Sie also die Adresse des ersten Zeichens der Zeichenfolge und die Adresse des letzten Zeichens hatten, können Sie die Länge der Zeichenfolge wie folgt ermitteln:

%Vor%

Eine solche Zeigerarithmetik ist type aware , was bedeutet, dass das Ergebnis der Arithmetik die Anzahl der Elemente - des bestimmten Typs - zwischen zwei Zeigern darstellt. Im obigen Beispiel mit char ist die Differenz die Anzahl der Zeichen. Dies funktioniert ähnlich für z.B. Zeiger auf zwei structs .

In ähnlicher Weise vergleicht Ihr zweiter Ausdruck einfach Zeiger und das Ergebnis ist 1 oder 0. Als ein sehr einfaches Beispiel ist die Adresse des Elements 5 eines Arrays immer > die Adresse von element 4 : &string[4] > &string[5] ist wahr.

    
pb2q 29.07.2012 23:49
quelle
-2

Sie können eine Adresse wie int auf viele Arten behandeln. Der einzige Unterschied ist, dass int die Anzahl der Größen in dieser Adresse darstellt. Zum Beispiel, wenn int * p zufällig den Wert von, sagen wir, 234 (von einem sicheren Befehl von zum Beispiel p = new int[12]; ) hat, stellt es die Adresse 234 dar. Wenn wir p += 1; machen, wird nur hinzugefügt, in Begriffe der int-Größe. Jetzt ist p (angenommen 4 Byte int für dieses Beispiel) 238, aka p[1] . Tatsächlich ist p[x] gleichbedeutend mit *(p+x) . Sie können vergleichen und so wie ein Int. In einigen Kontexten ist dies nützlich, zum Beispiel in dem gegebenen Beispiel bezieht sich p[0] jetzt auf was p[1] war. Dies vermeidet, etwas wie p = &p[1] zu tun, das unnötig dereferenziert.

    
Cosine 30.07.2012 00:21
quelle

Tags und Links