Zeigersubtraktionsverwirrung

Lesezeit: 7 Minuten

Wenn wir einen Zeiger von einem anderen Zeiger subtrahieren, ist die Differenz nicht gleich, wie viele Bytes sie voneinander entfernt sind, sondern gleich, wie viele Ganzzahlen (wenn auf Ganzzahlen zeigen) sie voneinander entfernt sind. Warum so?

  • es ist eine Abstraktion. Auf diese Weise müssen Sie die Größe des Objekts, auf das der Zeiger zeigt, nicht kennen, Sie können sich darauf verlassen, dass p– oder p++ zum letzten oder nächsten Objekt wechselt.

    – Ed S.

    13. Juli 2010 um 20:06 Uhr

Benutzeravatar von corsiKa
corsiKa

Die Idee ist, dass Sie auf Speicherblöcke zeigen

+----+----+----+----+----+----+
| 06 | 07 | 08 | 09 | 10 | 11 | mem
+----+----+----+----+----+----+
| 18 | 24 | 17 | 53 | -7 | 14 | data
+----+----+----+----+----+----+

Wenn Sie haben int* p = &(array[5]) dann *p wird 14. Gehen p=p-3 machen würden *p 17 sein.

Also, wenn Sie haben int* p = &(array[5]) und int *q = &(array[3])dann p-q sollte 2 sein, da die Zeiger auf Speicher zeigen, die 2 Blöcke voneinander entfernt sind.

Beim Umgang mit Rohspeicher (Arrays, Listen, Karten usw.) zeichnen Sie viele Kästchen! Es hilft wirklich!

  • Übrigens, wenn ich eines betonen darf, pointers are hard. Ja, erfahrene C++-Veteranen können sie mit Leichtigkeit auf Vordermann bringen, aber genau zu lernen, wann und wie sie effektiv eingesetzt werden, ist keine einfache Aufgabe.

    – corsiKa

    13. Juli 2010 um 15:11 Uhr

  • @glowcoder: Erfahrene C-Codierer können das auch – was in einer Frage mit dem Tag C vielleicht zutreffender ist.

    – Jonathan Leffler

    13. Juli 2010 um 15:15 Uhr

  • @Neil: FWIW, ich musste ziemlich viel nachdenken, um Hinweise zu “bekommen”, und ich hatte das Gefühl, mindestens Monate danach etwas nicht ganz Normales zu tun. Dies geschah, nachdem ich vier Assemblersprachen gelernt hatte, also war es kein Mangel an Wissen über den zugrunde liegenden Mechanismus. Dies war natürlich in Pascal, wo Hinweise wie nachträgliche Einfälle aussahen und in den Büchern eher nicht oft erwähnt wurden.

    – David Thornley

    13. Juli 2010 um 15:21 Uhr

  • @David Ich habe Assembler vor C gelernt und der Übergang schien sehr einfach. Ich habe jedoch immer noch Schwierigkeiten damit, wenn ich zu Delphi zurückkehre – hauptsächlich versuche ich, mich an die miserable Syntax zu erinnern.

    anon

    13. Juli 2010 um 15:23 Uhr

  • @glowcoder: du brauchst die geschweiften Klammern nicht, zB int *p = &array[5]; ist vollkommen in Ordnung, so wie es ist int *p = array + 5;.

    – PaulR

    13. Juli 2010 um 15:45 Uhr


Benutzeravatar von eruciform
erukiform

Denn alles im Zeigerland dreht sich um Offsets. Wenn du sagst:

int array[10];
array[7] = 42;

Was Sie eigentlich in der zweiten Zeile sagen, ist:

*( &array[0] + 7 ) = 42;

Wörtlich übersetzt als:

* = "what's at"
(
  & = "the address of"
  array[0] = "the first slot in array"
  plus 7
)
set that thing to 42

Und wenn wir 7 addieren können, um den Versatzpunkt an die richtige Stelle zu bringen, müssen wir in der Lage sein, das Gegenteil an Ort und Stelle zu haben, sonst haben wir keine Symmetrie in unserer Mathematik. Wenn:

&array[0] + 7 == &array[7]

Dann, für Vernunft und Symmetrie:

&array[7] - &array[0] == 7

Damit die Antwort auch auf Plattformen, auf denen ganze Zahlen unterschiedlich lang sind, dieselbe ist.

  • Also, wie kann ich mich an diese stinkende Tatsache erinnern 🙁 Macht mich tot 🙁

    Benutzer379888

    13. Juli 2010 um 15:06 Uhr

  • Aber wenn Sie eine Addition in Zeigern wie * p + 2 durchführen, wenn es sich um einen int-Zeiger handelt, bewegt er sich um 4 Positionen vorwärts. Warum?

    Benutzer379888

    13. Juli 2010 um 15:09 Uhr

  • @fahad: Wenn Sie die Byteanzahl zwischen zwei Adressen möchten, können Sie die beiden Adressen umwandeln char * und dann die Differenz nehmen. Woher kommt Ihnen die Idee, dass die Alternative besser ist? Hast du vor C in Assembler programmiert? Der C-Mechanismus ist solide – die Array-Indizierung hängt davon ab (weil eine Subskriptionsoperation N zur Basisadresse hinzufügt, aber das sind N Einheiten der Größe des Arrays).

    – Jonathan Leffler

    13. Juli 2010 um 15:10 Uhr

  • @fahad – * p + 2 fügt dem Inhalt des Speichers, auf den p zeigt, 2 hinzu. *(p+2) Sie können auf den Inhalt des Speichers zugreifen, auf den durch (p + 2*sizeof(datatype of p)) verwiesen wird. Es hängt vom Datentyp des Zeigers p ab.

    – Praveen S

    13. Juli 2010 um 15:14 Uhr

  • es macht dem Programmierer das Leben viel einfacher, bedenken Sie, wie viel mehr Arbeit es wäre, wenn es die Alternative wäre: wenn sich etwas am Programm ändern würde oder Sie es auf verschiedenen Systemen korrekt zum Laufen bringen möchten …

    – Emil Vrijdags

    13. Juli 2010 um 15:19 Uhr


Angenommen, Sie haben ein Array von 10 Ganzzahlen:

int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Dann nehmen Sie einen Zeiger auf intArray:

int *p = intArray;

Dann inkrementierst du p:

p++;

Was Sie erwarten würden, weil p startet um intArray[0]steht für den inkrementierten Wert von p sein intArray[1]. Deshalb funktioniert Zeigerarithmetik so. Siehe den Code hier.

“Wenn Sie zwei Zeiger subtrahieren, ist das Ergebnis die Anzahl der Elemente, die sie trennen, solange sie auf dasselbe Array zeigen.”

Suchen Sie nach mehr hier.

  • schönes Zitat 🙂 Ich möchte seine Erklärung ;P

    Benutzer379888

    13. Juli 2010 um 15:13 Uhr

  • @fahad: Da Zeiger nicht unbedingt auf Bytes zeigen, zeigen sie auf Objekte des Typs, der zu ihrer Definition verwendet wurde. Die Zeigerarithmetik wird auch in Bezug auf die Anzahl dieser Objekte durchgeführt.

    – JeremyP

    13. Juli 2010 um 15:17 Uhr

  • @fahad – Wahrscheinlich möchten Sie uns sagen, wie groß die grundlegenden Datentypen Ihrer Meinung nach sind? int, char und float beginnen. Auf dieser Grundlage können Sie sich mit der Zeigerarithmetik befassen, die sich von der normalen Arithmetik unterscheidet. (nicht vollständig, aber es hat seine Regeln ;))

    – Praveen S

    13. Juli 2010 um 15:25 Uhr

Auf diese Weise verhält sich die Zeigersubtraktion konsistent mit dem Verhalten der Zeigeraddition. Das bedeutet es p1 + (p2 - p1) == p2 (wo p1 und p2 sind Zeiger auf dasselbe Array).

Zeigeraddition (Hinzufügen einer ganzen Zahl zu einem Zeiger) verhält sich ähnlich: p1 + 1 gibt Ihnen die Adresse des nächsten Elements im Array und nicht das nächste Byte im Array – was eine ziemlich nutzlose und unsichere Sache wäre.

Die Sprache hätte so entworfen werden können, dass Zeiger auf die gleiche Weise wie ganze Zahlen addiert und subtrahiert werden, aber es hätte bedeutet, Zeigerarithmetik anders zu schreiben und die Größe des Typs zu berücksichtigen, auf den gezeigt wird:

  • p2 = p1 + n * sizeof(*p1) Anstatt von p2 = p1 + n
  • n = (p2 - p1) / sizeof(*p1) Anstatt von n = p2 - p1

Das Ergebnis wäre also Code, der länger und schwerer zu lesen ist und in dem leichter Fehler gemacht werden können.

  • schönes Zitat 🙂 Ich möchte seine Erklärung ;P

    Benutzer379888

    13. Juli 2010 um 15:13 Uhr

  • @fahad: Da Zeiger nicht unbedingt auf Bytes zeigen, zeigen sie auf Objekte des Typs, der zu ihrer Definition verwendet wurde. Die Zeigerarithmetik wird auch in Bezug auf die Anzahl dieser Objekte durchgeführt.

    – JeremyP

    13. Juli 2010 um 15:17 Uhr

  • @fahad – Wahrscheinlich möchten Sie uns sagen, wie groß die grundlegenden Datentypen Ihrer Meinung nach sind? int, char und float beginnen. Auf dieser Grundlage können Sie sich mit der Zeigerarithmetik befassen, die sich von der normalen Arithmetik unterscheidet. (nicht vollständig, aber es hat seine Regeln ;))

    – Praveen S

    13. Juli 2010 um 15:25 Uhr

Wenn Sie arithmetische Operationen auf Zeiger eines bestimmten Typs anwenden, möchten Sie immer, dass der resultierende Zeiger auf eine “gültige” (dh die richtige Schrittgröße) Speicheradresse relativ zum ursprünglichen Startpunkt zeigt. Das ist eine sehr bequeme Möglichkeit, unabhängig von der zugrunde liegenden Architektur auf Daten im Speicher zuzugreifen.

Wenn Sie eine andere “Schrittweite” verwenden möchten, können Sie den Zeiger jederzeit auf den gewünschten Typ werfen:

int a = 5;
int* pointer_int = &a;
double* pointer_double = (double*)pointer_int; /* totally useless in that case, but it works */

  • “völlig nutzlos” richtig! “aber es funktioniert” vielleicht auf Ihrem Compiler, heute … aber Der Standard verlangt nichts außer UB zum Dereferenzieren eines (nicht-[unsigned] char) Zeiger auf Speicher, der tatsächlich für einen nicht verwandten Typ zugewiesen wurde. auch wenn double hat eine strengere Ausrichtung als intdann könnte dies eine Falle erzeugen (oder wieder jede andere UB).

    – Unterstrich_d

    16. April 2016 um 22:14 Uhr

1413740cookie-checkZeigersubtraktionsverwirrung

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy