2016-04-14 5 views
2

Wenn ich zwei ungültige Zeiger subtrahiere, bekomme ich den relativen Abstand in Bytes (mindestens auf meinem Testsystem, das ich tue) zwischen den Zeigerstandorten. In welchem ​​Typ sollte ich das Ergebnis speichern, damit es mit 64-Bit-Systemen kompatibel ist? Ist size_t der richtige Typ oder vielleicht lang?C: Welcher Typ ist der Unterschied zwischen zwei ungültigen Zeigern?

Hintergrund: Wir müssen überprüfen, ob ein gegebener void pointer sicher ist, als Doppelzeiger benutzt zu werden. Wir haben den Zeiger auf int gesetzt und geprüft, dass die unteren drei Bits Null sind, aber unsere aktuellen Codierungsstandards erlauben es nicht, Zeiger auf Integertypen zu werfen. Ich überlege, den Unterschied zwischen dem void-Zeiger und dem NULL-Zeiger zu berechnen und zu überprüfen, ob dieser Unterschied durch 8 teilbar ist. Angenommen, dass der NULL-Zeiger immer 8-Byte ausgerichtet ist?

+0

"unsere aktuellen Codierungsstandards erlauben es nicht mehr, Zeiger auf ganzzahlige Typen zu setzen." -> Schade, dass du nicht nach "uintptr_t" umwandeln kannst. – chux

Antwort

4

Von C11, 6.5.6 Additive operators /9 (my kursiv gedruckt):

Wenn zwei Zeiger subtrahiert werden, die beide zu den Elementen des gleichen Array Objekt zeigen soll, oder eine hinter dem letzten Element des Arrays Objekts; Das Ergebnis ist die Differenz der Indizes der beiden Array-Elemente. Die Größe des Ergebnisses ist implementierungsdefiniert und sein Typ (ein vorzeichenbehafteter ganzzahliger Typ) ist ptrdiff_t definiert in der <stddef.h> Kopfzeile.

von diesem Abschnitt, ist es auch technisch nicht definiertes Verhalten, da NULL aus einem Zeiger subtrahieren NULL in keine Weise kann darüber hinaus einen Zeiger auf ein Element der gleichen Anordnung in Betracht gezogen werden, oder einem. Die Zeiger-Subtraktion ist tatsächlich darauf beschränkt, die Indexdifferenz zwischen zwei Elementen in demselben Array zu erhalten.

Vielleicht möchten Sie diese Überprüfung noch einmal überprüfen, insbesondere angesichts der Tatsache, dass ein Double nicht wirklich acht Byte lang sein muss, und selbst wenn dies der Fall ist, gibt es im Standard keine Anforderung es wird auf einer Acht-Byte-Grenze ausgerichtet.

Ich wäre eher geneigt, in der Dokumentation für Ihre Funktion einfach anzugeben, dass der übergebene Wert ein gültiger double Zeiger sein muss und wenn ein Benutzer Ihrer Funktion gegen diesen Vertrag verstößt, sind alle Wetten deaktiviert.

+0

Auf unserem System (PowerPC-basiert) muss ein Double an einer 8-Byte-Grenze ausgerichtet werden. Ich verstehe, dass der Check in sich nicht tragbar ist, aber es ist dennoch notwendig. – slingeraap

+0

@slingeraap Neugierig: Was macht Ihr Code, wenn der Zeiger nicht "doppelt" ausgerichtet ist? – chux

4

Wenn ich zwei void-Zeiger subtrahieren, erhalte ich den relativen Abstand in Bytes (zumindest auf meinem Testsystem I do) zwischen den Zeiger Standorten.

Es gibt eine strikte Regel, um zwei Zeiger zu subtrahieren. C11-§6.5.6:

Wenn zwei Zeiger subtrahiert werden, die beide zu den Elementen des gleichen Array Objekt zeigen soll, oder eine hinter dem letzten Element des Arrays Objekts; Das Ergebnis ist die Differenz der Indizes der beiden Array-Elemente. [...]


welche Art ist der Unterschied von zwei void-Zeiger?

Es sollte ptrdiff_t sein.

[...], um die Größe des Ergebnisses ist die Implementierung definiert, und sein Typ (a vorzeichenbehaftete Ganzzahl-Typ) ist in dem ptrdiff_t<stddef.h> Kopf definiert. [...]

5

Die Subtraktion von void pointers ist im C-Standard nicht definiert. Die Subtraktion von Zeigern wird nur definiert, wenn beide in dasselbe Array zeigen und ein void-Zeiger nicht in ein Array zeigen kann, da Sie kein Array mit void-Elementen haben können.

Höchstwahrscheinlich verwenden Sie gcc oder etwas Kompatibles und erlaubt Arithmetik auf void-Zeiger als eine Erweiterung, die es als Zeichenzeiger behandelt. Ein strikter C-Compiler sollte jedoch Zeigerarithmetik auf void-Zeiger nicht erlauben.

+0

Wir verwenden gcc in der Tat. – slingeraap

+1

@slingeraap Dann konfigurieren Sie es als einen richtigen C-Compiler: '-std = c11 -pedantic-errors'. Sie erhalten Compiler-Fehler, wenn Sie versuchen, Nicht-Standard-Mist zu tun. – Lundin

2

Zusammenfassung der Probleme:

  • Sie die Null-Zeiger Konstante Makro NULL mit Null-Zeiger verwirrend. Das sind zwei verschiedene Begriffe. Das NULL-Makro ist immer gleich 0, aber ein Null-Zeiger kann einen beliebigen Wert haben. See this.

  • Das besagt, es macht keinen Sinn, Zeigerarithmetik mit Null-Zeigern noch mit dem NULL-Makro zu tun. Dies würde zu undefiniertem Verhalten führen, da die Zeiger auf das gleiche Array zeigen müssen. Siehe die Antwort von paxdiablo.

  • Darüber hinaus, wie in der Antwort von Art erwähnt, können Sie keine Form der Arithmetik auf Void-Zeiger. Die Operatoren + und - werden als (6.5.6) angegeben:

    für Addition, entweder beiden Operanden werden arithmetischen Typen haben, oder ein Operand wird ein Zeiger auf ein vollständiges Objekttyp sein, und die andere wird haben ganzzahliger Typ

    Ein void-Zeiger ist ein Zeiger auf einen unvollständigen Typ, so dass, wenn einer oder beide Operanden ungültig Zeiger sind, Ihr Code ist nicht gültig C.

    Beachten Sie, dass GCC einen Nicht-Standard-Erweiterung hat, die void-Zeiger erlaubt arithmetisch, indem für solche Fälle Leerzeiger als Zeiger-zu-Byte behandelt werden. Verwenden Sie diese nicht standardmäßige Funktion niemals, wenn Sie sichere und portable Programme schreiben möchten. Kompilieren Sie mit gcc -std=c11 -pedantic-errors, wenn Sie möchten, dass sich GCC wie ein Standard-C-Compiler verhält (konforme Implementierung).