übergeben - zeiger c++




Zeigerarithmetik mit zwei verschiedenen Puffern (3)

Betrachten Sie den folgenden Code:

int* p1 = new int[100];
int* p2 = new int[100];
const ptrdiff_t ptrDiff = p1 - p2;

int* p1_42 = &(p1[42]);
int* p2_42 = p1_42 + ptrDiff;

Garantiert der Standard nun, dass p2_42 auf p2[42] ? Wenn nicht, trifft dies immer auf Windows-, Linux- oder Webassembly-Heap zu?


Der Standard ermöglicht Implementierungen auf Plattformen, bei denen der Speicher in diskrete Bereiche unterteilt ist, die mit Zeigerarithmetik nicht voneinander erreicht werden können. Als einfaches Beispiel verwenden einige Plattformen 24-Bit-Adressen, die aus einer 8-Bit-Banknummer und einer 16-Bit-Adresse innerhalb einer Bank bestehen. Das Hinzufügen von Eins zu einer Adresse, die das letzte Byte einer Bank identifiziert, ergibt einen Zeiger auf das erste Byte derselben Bank und nicht auf das erste Byte der nächsten Bank. Dieser Ansatz ermöglicht die Berechnung von Adressarithmetik und Offsets unter Verwendung von 16-Bit-Mathematik anstelle von 24-Bit-Mathematik, erfordert jedoch, dass kein Objekt eine Bankgrenze überspannt. Ein solches Design würde malloc eine gewisse zusätzliche Komplexität auferlegen und würde wahrscheinlich zu einer größeren Speicherfragmentierung führen, als dies sonst der Fall wäre, aber der Benutzercode müsste sich im Allgemeinen nicht um die Aufteilung des Speichers in Bänke kümmern.

Viele Plattformen haben keine derartigen architektonischen Einschränkungen, und einige Compiler, die für die Programmierung auf niedriger Ebene auf solchen Plattformen ausgelegt sind, ermöglichen die Ausführung einer Adressarithmetik zwischen beliebigen Zeigern. Der Standard stellt fest, dass eine übliche Methode zur Behandlung von undefiniertem Verhalten darin besteht, "sich während der Übersetzung oder Programmausführung in einer dokumentierten Weise zu verhalten, die für die Umgebung charakteristisch ist", und dass die Unterstützung für verallgemeinerte Zeigerarithmetik in Umgebungen, die dies unterstützen, gut in diese Kategorie passt. Leider bietet der Standard keine Möglichkeit zur Unterscheidung von Implementierungen, die sich so nützlich verhalten, und solchen, die dies nicht tun.


Die dritte Zeile ist Undefiniertes Verhalten. Der Standard lässt also alles zu, was danach folgt.

Es ist nur zulässig, zwei Zeiger zu subtrahieren, die auf (oder nach) dasselbe Array zeigen.

Windows oder Linux sind nicht wirklich relevant. Compiler und besonders ihre Optimierer sind das, was Ihr Programm bricht. Beispielsweise kann ein Optimierer erkennen, dass p1 und p2 beide auf den Anfang eines int[100] sodass p1-p2 0 sein muss.


const ptrdiff_t ptrDiff = p1 - p2;

Dies ist undefiniertes Verhalten. Die Subtraktion zwischen zwei Zeigern ist nur dann gut definiert, wenn sie auf Elemente in demselben Array zeigen. ( [expr.add] ¶5.3 ).

Wenn zwei Zeigerausdrücke P und Q subtrahiert werden, ist der Typ des Ergebnisses ein durch die Implementierung definierter vorzeichenbehafteter Integraltyp. Dieser Typ muss derselbe sein, der im Header <cstddef> ([support.types]) als std::ptrdiff_t definiert ist.

  • Wenn P und Q beide zu Null-Zeigerwerten ausgewertet werden, ist das Ergebnis 0.
  • Wenn andernfalls P und Q auf Elemente x[i] und x[j] desselben Array-Objekts x , hat der Ausdruck P - Q den Wert i−j .
  • Ansonsten ist das Verhalten undefiniert

Und selbst wenn es eine hypothetische Möglichkeit gab, diesen Wert auf legale Weise zu erhalten, ist selbst diese Summierung illegal, da selbst eine Zeiger- und Ganzzahlsummierung darauf beschränkt ist, innerhalb der Grenzen des Arrays zu bleiben ( [expr.add] ¶4.2 )

Wenn ein Ausdruck J mit einem ganzzahligen Typ zu einem Ausdruck P vom Zeigertyp hinzugefügt oder von diesem subtrahiert wird, hat das Ergebnis den Typ P

  • Wenn P einen Nullzeigerwert ergibt und J einen Wert von 0 ergibt, ist das Ergebnis ein Nullzeigerwert.
  • Andernfalls zeigen, wenn P auf das Element x[i] eines Array-Objekts x mit n Elementen zeigt, 81 die Ausdrücke P + J und J + P (wobei J den Wert j ) auf das (möglicherweise hypothetische) Element x[i+j] wenn 0≤i+j≤n x[i+j] 0≤i+j≤n und der Ausdruck P - J zeigt auf das (möglicherweise hypothetische) Element x[i−j] wenn 0≤i−j≤n x[i−j] 0≤i−j≤n .
  • Ansonsten ist das Verhalten undefiniert.




pointer-arithmetic