Subtraktion zweier Zeiger IMMER definiert?
-
class X { int a; int b; }; int main() { X* p1 = reinterpret_cast<X*>(342); X* p2 = reinterpret_cast<X*>(452234); std::cout << (p2 - p1) << std::endl; test::wait("Press ENTER to exit..."); return 0; }Was auch X ist, ist dies immer definiert? Also gibt es da kein undefiniertes Verhalten?
Hintergrund ist der folgende:
bool Class::is_in_container(Object& obj) { if(m_objVector.empty()) { return false; } Object* first = &m_objVector[0]; Object* request = &obj; return request == first || (request > first && (request - first) < m_objVector.size()); };Grüssli
-
Kann undefiniert sein:
ISO/IEC 14882:2003 §5.7.6 schrieb:
When two pointers to elements of the same array object are subtracted [...] Unless both pointers point to elements of the same array object, or one past the last element of the array object, the behavior is undefined.
Wenn
&objalso jenseits&(*objVector.end())liegt, ist die Mimik undefiniert.
-
-
Nein, es ist nicht definiert. Schon allein der reinterpret_cast von int nach X* kann alles mögliche ergeben, weil vom Standard nichtmal garantiert ist, dass int und X* gleichviel Speicher belegen.
-
Das hat mit deiner Problematik garnichts zu tun. In dem Code den du als Hintergrund angegeben hast ist kein reinterpret_cast verwendet worden sondern nur Pointerarithmetik. Was du dort tust ist allerdings auch nicht immer definiert, da der Vergleich mittels operator > nicht immer definiert ist (nämlich nur, wenn beide Pointer in eine zusammenhängende Datenstruktur zeigen, sei die dynamisch allokiert oder eine automatische Variable). Gleiches gilt dann auch für die Differenz.
-
Du fragst ob die Differen definiert ist - die Antwort ist also nein. Schlauer wäre gewesen zu fragen "kann man das was ich vorhabe so umsetzen, oder gibts ne bessere Möglichkeit?". Dann hätte man dir nämlich z.B antworten können:
"Ja, es gibt ne bessere Möglichkeit. Schau mal unter www.cplusplus.com/reference/algorithm/find.html, damit kannst du das dann so umsetzen:
bool Class::is_in_container(Object const& obj) const //weder das Object noch das Objekt der Klasse werden verändert, also const wo es geht! { std::vector<Object>::const_iterator begin = m_objVector.begin(), end = m_objVector.end(); return std::find(begin, end, obj) != end; };es geht also einfacher als du vorhattest, Vorausgesetzt es gibt einen
bool operator==(Object const&, Object const&)."/edith: oder std::find_if wenn wirklich die Adressen gleich sein sollen (siehe später
)
-
-
pumuckl schrieb:
[...]
Das er std::find benutzen kann, weiss er ganz sicher. Da bin ich zuversichtlich. Ich denke mal, er will sich das Durchiterieren sparen. -> Premature Optimization.
Dravere, Dravere das hätte ich nicht von Dir gedacht *kopfschüttel* SCNR

-
@pumuckl & Tachyon,
Ich muss euch beiden leider unrecht geben, ich habe nämlich genau die Frage gestellt, welche das Problem darstellt, das ich hatte.
Tatsächlich habe ich es nicht so umgesetzt, weil diese Lösung bereits früh aussortiert wurde, zugunsten von besseren Lösungen. Es war eine von vielen Ideen aus einem Brainstorming und ich wollte einfach wissen, ob dies definiert ist oder nicht, da ich es selber nicht wusste. Es ist also eine rein theoretische Frage.Im übrigen, auch wenn es jetzt unsinnig ist, dies anzumerken, ist die Lösung mit
std::findnicht ganz daselbe. Bei meinem Beispiel prüfe ich nach, ob das Objekt im Vektor ist und nicht nur sein Zustand.
Aber danke für die Antwort und den Link pumuckl. Ich hatte ihn aber schon zwischengespeichert, weil ich den so gut finde

Und ich bin enttäuscht, dass ihr mir sowas unterstellen könnt, dass ich nicht mal
std::findkenne oder premature optimisation durchführe.
Grüssli
-
Dravere schrieb:
Und ich bin enttäuscht, dass ihr mir sowas unterstellen könnt, dass ich nicht mal
std::findkenne oder premature optimisation durchführe.
Oooh... Fühlst du dich in deiner Ehre als C++-Programmierer verletzt? :p
Zum Thema: Wieso ist die Differenz zweier Zeiger undefiniert? Zeiger speichern ja Adressen, also Integer. Man könnte ja auch
reinterpret_casten und dann eineint-Subtraktion durchführen...
-
najut, dann ist die frage mit dem rinterpret_cast immernoch zumindest irreführend, hättest gleich sagen können dass die Referenz auf das selbe physikalische Objekt zeigen soll. Wenn du op== allerdings so definierst, dass er genau dann true zurück gibt wenn die Adressen die gleichen sind und nicht die Inhalte, dann gehts mit std::find sehr wohl. Da man das aber von einem op== nicth verlangen kann, muss ein eigenen Prädikat her und std::find_if
//irgendwo, vielleicht gibts das schon in boost oder <functional>? template <typename T1, typename T2> struct CompAdress { bool operator() (T1 const& lhs, T2 const& rhs) { return &lhs == &rhs; //Zeigervergleich! } }; bool Class::is_in_container(Object const& obj) const { std::vector<Object>::const_iterator begin = m_objVector.begin(), end = m_objVector.end(); return std::find_if(begin, end, boost::bind(CompAdress<Object,Object>(),obj)) != end; };So in der Art, keine Gewähr für richtige Verwendung von boost::bind. Das kann man natürlich auch etwas vereinfachen wenn man nicht gleich Boost und templates auffahren will:
bool Class::is_in_container(Object const& obj) const { struct Cmp //(!)Lokale Klassen sind ein etwas unbekannteres Feature von C++ ;) { Cmp(Object const* const ptr) : ptr(ptr) {} bool operator()(Object const& o) const {return &o == ptr;} private: Object const* const ptr; }; std::vector<Object>::const_iterator begin = m_objVector.begin(), end = m_objVector.end(); return std::find_if(begin, end, Cmp(&obj)) != end; };
-
Nexus schrieb:
Zeiger speichern ja Adressen, also Integer.
Zwei Fehler.
Erstens: Zeiger sind ein arithmetischer Datentyp, mehr sagt der Standard nicht aus. Dass sie nur eine einzelne Adresse beinhalten ist zwar auf "normalen" Systemen der Fall, kann aber durchaus anders sein. Auf einem etwas exotischeren System das explizit mit Dingen wie z.B. Cluster mit verteiltem Speicher arbeitet, könnte der Compiler einen Zeiger z.B. als Datenstruktur implementieren, die als ein Element einen eindeutigen Bezeichner für die Clusterkomponente und als zweites Element eine übliche Adresse im Speicher dieser Komponente enthält. Damit wäre auch schon ein Beispiel geschaffen wo zwei Zeiger nicht mehr vergleichbar sind bzw. keine Differenzen gebildet werden können. Wenn sich beide Zeiger auf verschiedene Clusterkomponenten beziehen ist ein Vergleich der puren Adressen sinnfrei.
Zweitens: es ist wie schon gesagt nirgendwo garantiert, dass ein handelsüblicher Zeiger in der Größe einem Integer (oder genauer einem unsigned int) entspricht. Ein Compilerhersteller kann durchaus der Meinung sein, dass auf einem 64bit-System mit 8Byte-Zeigern ein int trotzdem noch 4 Byte groß sein sollte ("das war schon immer so..." *hust*). Das wäre völlig legal und im Sinne des Standards.
-
Danke für deine Antwort, pumuckl. Jetzt bin ich wirklich etwas erstaunt. Man lernt sogar über die grundlegendsten Dinge immer wieder dazu...

pumuckl schrieb:
Zeiger sind ein arithmetischer Datentyp, mehr sagt der Standard nicht aus. [...] könnte der Compiler einen Zeiger z.B. als Datenstruktur implementieren, die als ein Element einen eindeutigen Bezeichner für die Clusterkomponente und als zweites Element eine übliche Adresse im Speicher dieser Komponente enthält.
Mit Integer meinte ich nicht nur
int, sondern allgemein einen integralen Datentyp. Aber eine komplexere Datenstruktur ist ja kein arithmetischer Datentyp mehr?Und gibt es solche exotischen Systeme tatsächlich oder ist das ein nicht vorhandener, aber prinzipiell erlaubter Fall?
-
Nexus schrieb:
Aber eine komplexere Datenstruktur ist ja kein arithmetischer Datentyp mehr?
Eine Datenstruktur, wo einige Bytes als Mantisse, ein Bit als Vorzeichen und ein paar weitere Bytes als Exponent interpretiert werden mit den entsprechenden Algorithmen für arithmetische Operationen (die komplizierter sind als einfache Integeraddition etc.) nennt sich z.B. double
Was spricht dagegen, dass eine andere Datenstruktur, die z.B. aus 4 Bytes für eine systeminterne IP-ähnliche Komponentenadresse und 4 Bytes für einen 0/8/15 Pointer besteht, auch ein arithmetischer Typ sein kann? Zumal in dem Fall die arithmetischen Operationen explizit nur innerhalb bestimmter Untermengen definiert sind (z.B. wenn die 4 Bytes für die Komponentenadresse jeweils gleich sind...)Und gibt es solche exotischen Systeme tatsächlich oder ist das ein nicht vorhandener, aber prinzipiell erlaubter Fall?
Mal ganz ehrlich: so genau will ich das garnicht wissen obs sowas gibt

-
pumuckl schrieb:
Was spricht dagegen, dass eine andere Datenstruktur [...] auch ein arithmetischer Typ sein kann?
Naja, eigentlich nichts. Ich dachte bei Datenstruktur nur als erstes an Container-ähnliche Gebilde (z.B. in Klassen), gerade weil du noch von Elementen sprachst. Tut mir leid, dass ich es so genau genommen habe...

pumuckl schrieb:
Mal ganz ehrlich: so genau will ich das garnicht wissen obs sowas gibt

Hehe, mich hats nur gewundert - nicht dass es meinen Alltag jetzt grob beeinflussen würde...

Der Standard hätte von mir aus gesehen in vielen Dingen ein wenig strikter sein dürfen. Ich kann mir auch gut vorstellen, dass man trotz guter Kenntnis des C++-Standards ab und zu Undefiniertes tut, ohne es zu wissen. Da das aber in 99.9% aller Fälle gut geht, hat man auch keine Probleme damit und bleibt weiterhin im Unwissen...
-
Nachtrag:
INCITS+ISO+IEC+14882 schrieb:
1.7 The C+ + memory model [intro.memory]
1 The fundamental storage unit in the C + + memory model is the byte. A byte is at least large enough to contain
any member of the basic execution character set and is composed of a contiguous sequence of bits, the
number of which is implementation-defined. The least significant bit is called the low-order bit; the most
significant bit is called the high-order bit. The memory available to a C + + program consists of one or more
sequences of contiguous bytes. Every byte has a unique address.Betonung auf "one or more Sequence".
Ich muss ein wenig revidieren was ich vorher geschrieben hab: Der Standard sagt ausdrücklich, dass Pointervergleiche (§5.9) "unspecified" sind (im Gegensatz zu "undefined"), d.h. es kann nicht irgendwas beliebiges passieren (z.B. Absturz, Computer frisst die Katze etc.) sondern es kommt nur etwas beliebiges heraus. Die Differenz zwischen zwei Pointern, die nicht zum Membern des selben Objekts bzw. zu Elementen des selben Arrays zeigen und die nicht null sind, ist dagegen wirklich undefiniert (§5.7).
Der Standard spricht bei den Werten von Pointern auch explizit von Adressen (§3.9.2), aber die interne Repräsentation einer solchen Adresse kann für exotische Systeme eben auch exotisch komplex sein wie oben beschrieben. Das ist dann eben Sache des entsprechenden Systems, wie die Adressen repräsentiert werden.Pointer sind im Standard also keine arithmetischen Typen, sondern Compund Types, für die arithmetische Operationen utner den entsprechenden Umständen definiert sind.
-
pumuckl schrieb:
najut, dann ist die frage mit dem rinterpret_cast immernoch zumindest irreführend, hättest gleich sagen können dass die Referenz auf das selbe physikalische Objekt zeigen soll.
Naja, mir ging es halt um beliebige Zeiger. Aber hast eigentlich schon recht, der
reinterpret_castwar wohl etwas irreführend.
Aber ich dachte wohl, man würde mir nicht gleich solche Unterstellungen hinwerfen ... und vielleicht hatte ich es auch grad im Kopf und war etwas unvorsichtig mit der Formulierung. Mehr für logisch ersichtlich angenommen, als es tatsächlich war.pumuckl schrieb:
Wenn du op== allerdings so definierst, dass er genau dann true zurück gibt wenn die Adressen die gleichen sind und nicht die Inhalte, dann gehts mit std::find sehr wohl. Da man das aber von einem op== nicth verlangen kann, muss ein eigenen Prädikat her und std::find_if
Zwei Dinge hierzu:
1. Ich habe nicht gesagt, dass es nicht möglich wäre, nur das deine Lösung nicht ganz korrekt wäre.
Ich fühle mich wieder in meiner Ehre angegriffen, dass du denkst, ich wüsste sowas nicht ...

2. Man könnte jetzt aber GANZ PINGELIG sein und sagen, dassstd::find_ifnicht gleichstd::findist. Also hättest du denop==gefälligst überladen sollen :p
3. Nur um das vorauszugreifen. JA, derop==so zu überladen, wäre ÄUSSERST hässlich
Im übrigen noch Danke für den Nachtrag und Auszug aus dem Standard!
Grüssli
-
Tachyon schrieb:
Das er std::find benutzen kann, weiss er ganz sicher. Da bin ich zuversichtlich. [...]
Hmpf, ich habe extra geschrieben, dass ich Dir nicht zutraue, dass Du nichts von std::find weisst, und trotzdem muss man sich dann die bösen Vorwürfe von Dir anhören. Pfff.... :p
-
Tz, jetzt kommt noch Zensur ... aber ich habe das Zitat :p
Tachyon schrieb:
... Ich denke mal, er will sich das Durchiterieren sparen. -> Premature Optimization.
Dravere, Dravere das hätte ich nicht von Dir gedacht *kopfschüttel* SCNR

Ging mir halt gleich, konnte auch nicht widerstehen

Grüssli