[C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
[C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Hi,
angenommen ich habe zwei Arrays:
int foo[100];
int bar[50];
und einen Zeiger:
int * pX;
Nun würde ich gern herausfinden, zu welchem Array der Zeiger gehört:
if(pX >= foo && pX <= foo + 100)
…; // Zeiger liegt in foo
else if(pX >= bar && pX <= bar + 50)
…; // Zeiger liegt in bar
Ist das gültiges C++ oder dürfen Zeiger grundsätzlich nur mit dem Array verglichen werden, in dem das verwiesene Objekt tatsächlich liegt?
Das würde mir viel Arbeit sparen; ich will nur nicht, dass es jetzt zufällig funktioniert und ein zukünftiger Compiler die Vergleiche als undefiniertes Verhalten wegoptimiert.
Gruß
angenommen ich habe zwei Arrays:
int foo[100];
int bar[50];
und einen Zeiger:
int * pX;
Nun würde ich gern herausfinden, zu welchem Array der Zeiger gehört:
if(pX >= foo && pX <= foo + 100)
…; // Zeiger liegt in foo
else if(pX >= bar && pX <= bar + 50)
…; // Zeiger liegt in bar
Ist das gültiges C++ oder dürfen Zeiger grundsätzlich nur mit dem Array verglichen werden, in dem das verwiesene Objekt tatsächlich liegt?
Das würde mir viel Arbeit sparen; ich will nur nicht, dass es jetzt zufällig funktioniert und ein zukünftiger Compiler die Vergleiche als undefiniertes Verhalten wegoptimiert.
Gruß
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Es ist nicht undefined, aber unspecified:
ISO/IEC 14882:2011 §5.9/2 hat geschrieben:- If two pointers p and q of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results of p<q, p>q, p<=q, and p>=q are unspecified.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ist in Wikipedia sogar als Beispiel für unspecified behaviour gegeben … vielen Dank :-) In C ist es übrigens sogar undefined.
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ok, das ist jetzt ein Beispiel, das so trivial ist, dass ich nicht mal auf die Idee gekommen wäre nachzufragen. Ich sehe jetzt aber irgendwie immer noch nicht, was beim vergleichen eines Zeigers mit einem Array zu Problemen führen könnte. Kann mich jemand erleuchten?
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Das Problem ist, Zeiger, die in zwei verschiedene Objekte zeigen, zu vergleichen. Zwei Zeiger, die in das selbe Array zeigen, sind kein Problem... ;)
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Grundlage ist erst einmal, dass C++ keine Adressen kennt, sondern nur Zeiger. Adressen sind eine Art und Weise, wie man Zeiger implementieren kann (ein Implementation Detail) – aber nicht die Einzige.
Darum räumt der C++-Standard zum Beispiel ein, dass Zeiger je nach Objekt, auf das sie zeigen, unterschiedlich groß sein können: In einigen Implementierungen kann sizeof(int) != sizeof(char) gelten. Landläufig ist int oft vier Mal so groß wie char. Einem Compiler wäre damit erlaubt, int-Zeiger zwei Bits kleiner zu machen als char-Zeiger – weil vier Mal so viele chars in den Speicher passen wie ints, braucht man auch zwei Bits mehr (entsprechende Ausrichtung angenommen). reinterpret_cast<char *>(intPtr) müsste zwei Bits mehr reservieren und den Eingangswert mit vier Multiplizieren.
Das lehrt uns, dass Zeiger ziemlich abstrakte Dinge sind, und nicht immer bloß fortlaufende Speicheradressen. Das ist nötig weil nicht alle Architekturen mit einem einzigen fortlaufenden Adressraum arbeiten, sondern durchaus float in logisch anderen Bereichen (mit komplett anderer Art der Adressierung) speichern dürfen als int, oder Daten in anderen Bereichen als Code (das nennt man Harvard-Architektur), oder in Mehrprozessorsystemen jeder Prozessor seinen eigenen Speicher pflegen darf (NUMA). Oder Compiler können spezielle Debug-Funktionen anbieten, um jeden deiner Zugriffe auf Gültigkeit zu prüfen, und müssen dafür noch ndere (Debug-)Sachen in Zeiger stecken.
Folge davon ist, dass >, <, >=, und <= nur für Zeiger anwendbar ist, die im selben Array liegen. Falls eine Architektur z.B. Arrays, die größer als 1 KiB sind, in logisch komplett anderen Speicher steckt als kleine Arrays oder einzelne Objekte, lassen sie sich auch nicht vergleichen: Welche Art von Speicher wäre in diesem Fall „kleiner“? „Kleiner“ funktioniert nur bei Dingen, die sequenziell sind – was C++ eben bloß für einzelne Arrays garantiert, um auch stark heterogene Architekturen unterstützen zu können.
Darum räumt der C++-Standard zum Beispiel ein, dass Zeiger je nach Objekt, auf das sie zeigen, unterschiedlich groß sein können: In einigen Implementierungen kann sizeof(int) != sizeof(char) gelten. Landläufig ist int oft vier Mal so groß wie char. Einem Compiler wäre damit erlaubt, int-Zeiger zwei Bits kleiner zu machen als char-Zeiger – weil vier Mal so viele chars in den Speicher passen wie ints, braucht man auch zwei Bits mehr (entsprechende Ausrichtung angenommen). reinterpret_cast<char *>(intPtr) müsste zwei Bits mehr reservieren und den Eingangswert mit vier Multiplizieren.
Das lehrt uns, dass Zeiger ziemlich abstrakte Dinge sind, und nicht immer bloß fortlaufende Speicheradressen. Das ist nötig weil nicht alle Architekturen mit einem einzigen fortlaufenden Adressraum arbeiten, sondern durchaus float in logisch anderen Bereichen (mit komplett anderer Art der Adressierung) speichern dürfen als int, oder Daten in anderen Bereichen als Code (das nennt man Harvard-Architektur), oder in Mehrprozessorsystemen jeder Prozessor seinen eigenen Speicher pflegen darf (NUMA). Oder Compiler können spezielle Debug-Funktionen anbieten, um jeden deiner Zugriffe auf Gültigkeit zu prüfen, und müssen dafür noch ndere (Debug-)Sachen in Zeiger stecken.
Folge davon ist, dass >, <, >=, und <= nur für Zeiger anwendbar ist, die im selben Array liegen. Falls eine Architektur z.B. Arrays, die größer als 1 KiB sind, in logisch komplett anderen Speicher steckt als kleine Arrays oder einzelne Objekte, lassen sie sich auch nicht vergleichen: Welche Art von Speicher wäre in diesem Fall „kleiner“? „Kleiner“ funktioniert nur bei Dingen, die sequenziell sind – was C++ eben bloß für einzelne Arrays garantiert, um auch stark heterogene Architekturen unterstützen zu können.
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Danke für die ausführliche Antwort! Kennst du irgendwelche Architekturen, die unterschiedliche Speicherbereiche für bestimmte Datentypen/Array-Größen verwenden?
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Keine Ahnung – ich denke, dass du da bei Mikrocontrollern und alten Spielekonsolen am ehesten fündig wirst.
- Sternmull
- Establishment
- Beiträge: 264
- Registriert: 27.04.2007, 00:30
- Echter Name: Til
- Wohnort: Dresden
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Jetzt frag ich mich natürlich wie sowas wie memmove dann effizient feststellen soll ob sich der Speicherbereich von Ziel und Quelle überlappt.
Und ganz ehrlich: Ich würde an der Stelle nicht auf den Standard achten und die Tatsache nutzen das es in der Praxis funktioniert.
Und ganz ehrlich: Ich würde an der Stelle nicht auf den Standard achten und die Tatsache nutzen das es in der Praxis funktioniert.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Beachte: Es ist unspecified, nicht undefined. Unspecified heißt, dass ein Programm das tun darf, nur das Ergebnis der jeweiligen Implementierung überlassen ist (aber es gibt ein Ergebnis!). memmove ist Teil der Implementierung und wird natürlich alle Eigenschaften der jeweiligen Architektur ausnutzen, wie z.B. dass Pointer am Ende nur Integer sind...
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Sternmull hat geschrieben:Jetzt frag ich mich natürlich wie sowas wie memmove dann effizient feststellen soll ob sich der Speicherbereich von Ziel und Quelle überlappt.
- memmove() ist im Standard damit du eine portable Lösung benutzen kannst statt dich selber um „Überlappen die sich?“ auf bestimmten Plattformen kümmern zu müssen.
- memmove() kann immer ganz strunzdoof als Byte-weise Kopie implementiert sein. Dass memmove() schneller sein muss sobald sich die Bereiche nicht überlappen ist nirgends vorgeschrieben.
Dann guckst du nur dumm aus der Wäsche wenn die nächste Version deines C-Compilers die ifs als undefiniertes Verhalten schlicht und einfach löscht. In C++ ist es nicht spezifiziert, was wohl bedeutet, dass einem tatsächlich nichts anderes als Ausprobieren übrig bleibt :( Es hätte aber auch anders sein können (siehe C), darum habe ich gefragt.Und ganz ehrlich: Ich würde an der Stelle nicht auf den Standard achten und die Tatsache nutzen das es in der Praxis funktioniert.
Nachtrag: dot war schneller, und kürzer, und deutlicher :)
- TGGC
- Establishment
- Beiträge: 569
- Registriert: 15.05.2009, 18:14
- Benutzertext: Ich _bin_ es.
- Alter Benutzername: TGGC
- Echter Name: Ich _bin_ es.
- Wohnort: Mainz
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Sry, aber du erzaehlst Unsinn. Wenn das wirklich der Fall waere, so koennten die Vergleiche weiterhin problemlos funktionieren. Man koennte naemlich einfach den beschriebenen cast machen und dann vergleichen...Krishty hat geschrieben:Einem Compiler wäre damit erlaubt, int-Zeiger zwei Bits kleiner zu machen als char-Zeiger – weil vier Mal so viele chars in den Speicher passen wie ints, braucht man auch zwei Bits mehr (entsprechende Ausrichtung angenommen). reinterpret_cast<char *>(intPtr) müsste zwei Bits mehr reservieren und den Eingangswert mit vier Multiplizieren.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ein Beispiel wären wohl GPUs, da gibt's verschiedene Speicherbereiche für verschiedene Zwecke. Mittlerweile haben die aber auch schon einen Unified Address Space...dawit hat geschrieben:Danke für die ausführliche Antwort! Kennst du irgendwelche Architekturen, die unterschiedliche Speicherbereiche für bestimmte Datentypen/Array-Größen verwenden?
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ja; in diesem Fall würde das noch gehen. Das Beispiel sollte aber nur zeigen, dass Zeiger nicht nötigerweise homogene Adressen sind. Danach kam das Beispiel mit den getrennten Adressräumen, und dann funktionieren auch die char *-Casts nicht mehr.TGGC hat geschrieben:Sry, aber du erzaehlst Unsinn. Wenn das wirklich der Fall waere, so koennten die Vergleiche weiterhin problemlos funktionieren. Man koennte naemlich einfach den beschriebenen cast machen und dann vergleichen...Krishty hat geschrieben:Einem Compiler wäre damit erlaubt, int-Zeiger zwei Bits kleiner zu machen als char-Zeiger – weil vier Mal so viele chars in den Speicher passen wie ints, braucht man auch zwei Bits mehr (entsprechende Ausrichtung angenommen). reinterpret_cast<char *>(intPtr) müsste zwei Bits mehr reservieren und den Eingangswert mit vier Multiplizieren.
Und ein dickes Problem ist, dass man es nicht einfach bei undefiniertem Verhalten belassen, sondern den Anwender schon beim Kompilieren warnen möchte, wenn er was falsch macht. OpenCL C braucht zu diesem Zweck vier Address Space Qualifiers bei Zeigerdeklarationen: __local, __global, __constant, und __private.dot hat geschrieben:Ein Beispiel wären wohl GPUs, da gibt's verschiedene Speicherbereiche für verschiedene Zwecke. Mittlerweile haben die aber auch schon einen Unified Address Space...dawit hat geschrieben:Danke für die ausführliche Antwort! Kennst du irgendwelche Architekturen, die unterschiedliche Speicherbereiche für bestimmte Datentypen/Array-Größen verwenden?
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
du könntest sogar die Hälfte der Vergleiche rausschmeißen, da du ja weißt, in welcher Reihenfolge die Arrays auf dem Stack liegen ;)Krishty hat geschrieben:Hi,
angenommen ich habe zwei Arrays:
int foo[100];
int bar[50];
und einen Zeiger:
int * pX;
Nun würde ich gern herausfinden, zu welchem Array der Zeiger gehört:
if(pX >= foo && pX <= foo + 100)
…; // Zeiger liegt in foo
else if(pX >= bar && pX <= bar + 50)
…; // Zeiger liegt in bar
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Wegoptimiert wird es bestimmt nicht, da der Code für einen Außenstehenden wie ein Rangecheck aussieht. Und Rangechecks werden nur herausoptimiert werden, wenn die Sprache implizite Rangechecks schon drin hätte (managed Code), was aber bei C/C++ wohl nie der Fall werden wird.Krishty hat geschrieben:Das würde mir viel Arbeit sparen; ich will nur nicht, dass es jetzt zufällig funktioniert und ein zukünftiger Compiler die Vergleiche als undefiniertes Verhalten wegoptimiert.
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Doch; natürlich wird es das. Es gab mal ein Memo einer US-Behörde, die mit Range Checks gegen Pufferüberläufe sichern wollte, und dann blöd geguckt hat, als GCC die alle durch if(true) ersetzt hat. Clang optimiert undefiniertes Verhalten noch aggressiver weg (insbesondere Nullzeigerprüfungen). Darum sollte man solchen Quelltext unter C auf gar keinen Fall benutzen.
Dies:
if(pX >= foo && pX < foo + 100)
*pX = 0;
wird von modernen C-Optimizern so interpretiert:
*pX = 0; // if eliminiert
Dass es einen Stack gibt ist ein Implementierungsdetail und wird von C++ nirgends erwähnt. Es beginnt ja schon damit, dass der Stack auf x86 üblicherweise von hohen Adressen zu niedrigen wächst, während Arrays von niedrigen Adressen zu hohen verlaufen. Das erste Array liegt also hier bei mir höher im Speicher – es sei denn, ich packe es in ein Objekt; dann umgekehrt …
Dies:
if(pX >= foo && pX < foo + 100)
*pX = 0;
wird von modernen C-Optimizern so interpretiert:
- pX wird mit dem Array foo verglichen.
- Ein Zeiger darf mit anderen Zeigern nur verglichen werden, falls sie auf dasselbe Objekt (in diesem Fall: das Array foo) zeigen.
- Zeigt pX nicht auf ein Element von foo, ist das Programm undefiniert. Dann dürfen wir tun, was wir wollen.
- Wir entscheiden uns, dann das selbe zu tun wie in dem Fall, wo pX tatsächlich auf foo zeigt.
*pX = 0; // if eliminiert
Nichts garantiert, dass lokale Variablen in Deklarationsreihenfolge im Speicher liegen :( Denk daran, dass Stapelspeicher nur eine mögliche Art ist, die von C++ abstrakt vorgeschriebene Zerstörungsreihenfolge zu realisieren – aber nicht die einzige.antisteo hat geschrieben:du könntest sogar die Hälfte der Vergleiche rausschmeißen, da du ja weißt, in welcher Reihenfolge die Arrays auf dem Stack liegen ;)
Dass es einen Stack gibt ist ein Implementierungsdetail und wird von C++ nirgends erwähnt. Es beginnt ja schon damit, dass der Stack auf x86 üblicherweise von hohen Adressen zu niedrigen wächst, während Arrays von niedrigen Adressen zu hohen verlaufen. Das erste Array liegt also hier bei mir höher im Speicher – es sei denn, ich packe es in ein Objekt; dann umgekehrt …
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ein Compiler optimiert undefiniertes Verhalten weg? Das ist ja schrecklich. Ich würde erwarten, dass in diesem Fall ein Laufzeitfehler kommt und nicht einfach irgendwas am Verhalten geändert wird. Und bei deinem Code würde ich eigentlich so viel Intelligenz beim Compiler voraussetzen, dass er das eindeutig zu true oder false auflösen kann, ganz unabhängig ob das spezifiziert ist, denn es ist völlig unabhängig vom Speicherort oder der Implementierung möglich festzustellen, ob der Pointer Teil des Arrays ist oder nicht. Und wenn er dies nicht ist, dann ist diese Bedingung halt auch false. Das ist einfach ein gewisse Logik, die da von Nöten ist. Schöner wäre natürlich ein Operator ala ptr is_inside_of arr.
Ohne Input kein Output.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
In einem Programm mit undefiniertem Verhalten, gibt es kein Verhalten, an dem sich etwas ändern könnte, denn das Verhalten ist ja undefiniert...BeRsErKeR hat geschrieben:Ich würde erwarten, dass in diesem Fall ein Laufzeitfehler kommt und nicht einfach irgendwas am Verhalten geändert wird.
Moment, bei der Pointer in Array Sache ist das Verhalten (in C++) nur unspezifiziert, das ist etwas anderes als undefiniertes Verhalten. Im Falle von unspezifiziertem Verhalten, gibt es zumindest irgendein implementierungsabhängiges Verhalten, welches, je nach Implementierung, beispielsweise das von dir erwartete Verhalten sein könnte...BeRsErKeR hat geschrieben:Und bei deinem Code würde ich eigentlich so viel Intelligenz beim Compiler voraussetzen, dass er das eindeutig zu true oder false auflösen kann, ganz unabhängig ob das spezifiziert ist, denn es ist völlig unabhängig vom Speicherort oder der Implementierung möglich festzustellen, ob der Pointer Teil des Arrays ist oder nicht.
Es gibt keine allgemeinen Garantien bezüglich der relativen Platzierung verschiedener, unabhängiger Objekte im Speicher; spätestens bei Objekten, die am free store (heap) angelegt wurden, wirst du feststellen, dass es selbst im Rahmen einer konkreten Implementierung (z.b. MSVC auf x64) keinen Weg gibt, irgendwelche sinnvollen Garantien diesbezüglich überhaupt auch nur zu beschreiben. Ohne solche lässt sich allerdings für <, <=, > und >= kein sinnvolles Verhalten definieren...
Code: Alles auswählen
A* a = new A;
B* b = new B;
if (a < b)
// ???
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ich dachte das Verhalten ist nur dann undefiniert, wenn der Pointer eben nicht im Array liegt und das wird ja u.U. erst zur Laufzeit festgestellt. Der Code sollte ja sowohl in C als auch C++ definiert sein, falls man mit dem Array testet, in dem der Pointer tatsächlich liegt. Oder habe ich das falsch verstanden? Das heißt das Verhalten ändert sich je nachdem ob der Pointer im Array liegt oder nicht.dot hat geschrieben:In einem Programm mit undefiniertem Verhalten, gibt es kein Verhalten, an dem sich etwas ändern könnte, denn das Verhalten ist ja undefiniert...BeRsErKeR hat geschrieben:Ich würde erwarten, dass in diesem Fall ein Laufzeitfehler kommt und nicht einfach irgendwas am Verhalten geändert wird.
Ja aber in C wäre das ganze aus logischer Sicht genauso gut realisierbar. Der Compiler müsste halt erkennen, dass hier geprüft werden soll, ob der zeiger im Array liegt oder nicht. Ich will damit sagen, wenn der Compiler hier sowieso was wegoptimiert, dann könnte er doch durch etwas Logik hier so optimieren, dass er erkennt, was der Programmierer bezwecken will. Denn dann könnte er eindeutig true oder false liefern.dot hat geschrieben:Moment, bei der Pointer in Array Sache ist das Verhalten (in C++) nur unspezifiziert, das ist etwas anderes als undefiniertes Verhalten. Im Falle von unspezifiziertem Verhalten, gibt es zumindest irgendein implementierungsabhängiges Verhalten, welches, je nach Implementierung, beispielsweise das von dir erwartete Verhalten sein könnte...BeRsErKeR hat geschrieben:Und bei deinem Code würde ich eigentlich so viel Intelligenz beim Compiler voraussetzen, dass er das eindeutig zu true oder false auflösen kann, ganz unabhängig ob das spezifiziert ist, denn es ist völlig unabhängig vom Speicherort oder der Implementierung möglich festzustellen, ob der Pointer Teil des Arrays ist oder nicht.
Ja wenn man das allgemein auf Vergleichsoperatoren bezieht mag das ja stimmen, aber Vergleichsoperatoren auf Pointer dienen doch eigentlich immer zur Feststellung, ob der Pointer in einem bestimmten Bereich liegt (relativ zu einem anderen Pointer/Array). Wenn ich also prüfe ob Pointer x im (zusammenhängenden) Bereich y liegt, dann ist es völlig egal wo der liegt. Es ist immer eindeutig möglich festzustellen, ob x im Bereich y liegt oder nicht. Man vergleicht ja relativ mit einem Bereich und nicht absolut. Die Sprachmittel von C/C++ sind hier vielleicht etwas ungeeignet, aber von der Theorie her ist dieser Vergleich absolut unproblematisch, sofern man mit Bereichen prüft, die sequenziell hintereinander liegen (was Arrays ja bekanntlich tun).dot hat geschrieben:Es gibt keine allgemeinen Garantien bezüglich der relativen Platzierung verschiedener, unabhängiger Objekte im Speicher; spätestens bei Objekten, die am free store (heap) angelegt wurden, wirst du feststellen, dass es selbst im Rahmen einer konkreten Implementierung (z.b. MSVC auf x64) keinen Weg gibt, irgendwelche sinnvollen Garantien diesbezüglich überhaupt auch nur zu beschreiben. Ohne solche lässt sich allerdings für <, <=, > und >= kein sinnvolles Verhalten definieren...
Code: Alles auswählen
A* a = new A; B* b = new B; if (a < b) // ???
Ich meine damit, dass der Compiler die Prüfung im if einfach in eine relative Bereichsprüfung umoptimieren könnte, die dann in jedem Fall eindeutig ist. Das Problem mit der Undefiniertheit kommt ja nur durch die Forderung nach einer absoluten Positionierung zu stande. Eine Prüfung darauf, ob ein Pointer innerhalb eines Bereiches liegt, ist ja einfach lösbar, indem zwischen Anfang und Ende des Bereiches nach dem Pointer gesucht wird. Dazu ist ggf. ein Mapping in den jeweiligen realen Speicherbereich erforderlich, undefiniert ist es dann aber nicht mehr.
Ich weiß, dass das natürlich dann nicht gilt, wenn man einfach nur auf kleiner oder größer prüft, aber wozu sollte man prüfen ob ein Pointer kleiner ist als ein anderer, wenn er nicht innerhalb des selben Objekts / Arrays liegt? Hier ist unspezifiziertes Verhalten wahrscheinlich ok, aber eine Bereichsprüfung könnte ein Compiler immer eindeutig auflösen.
Ohne Input kein Output.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ja, ein konkreter Compiler auf einer konkreten Architektur wird dies in vielen Fällen erst zur Laufzeit feststellen können...das heißt nicht, dass das im Allgemeinen so ist. Im Allgemeinen lässt sich das nicht klar sagen...BeRsErKeR hat geschrieben:Ich dachte das Verhalten ist nur dann undefiniert, wenn der Pointer eben nicht im Array liegt und das wird ja u.U. erst zur Laufzeit festgestellt.
So lange die beiden verglichenen Pointer in das selbe Objekt zeigen, ist das Verhalten sowohl in C als auch in C++ definiert, denk ich. Problematisch wird es, sobald die Pointer eben nichtmehr beide in das selbe Objekt zeigen. Der Fall ist in C undefiniert und in C++ unspezifiziert.BeRsErKeR hat geschrieben:Der Code sollte ja sowohl in C als auch C++ definiert sein, falls man mit dem Array testet, in dem der Pointer tatsächlich liegt. Oder habe ich das falsch verstanden? Das heißt das Verhalten ändert sich je nachdem ob der Pointer im Array liegt oder nicht.
Nichts hindert den Compiler daran, dies zu tun, wenn er es kann. Es ist dennoch undefiniertes Verhalten, d.h. ein anderer Compiler darf beim Übersetzen von solchem Code stattdessen gerne auch lustige Musik spielen und Feuer fangen, eine exe produzieren, die nur nicht crashed, wenn Dienstag in einer Woche Vollmond ist etc.BeRsErKeR hat geschrieben:Ja aber in C wäre das ganze aus logischer Sicht genauso gut realisierbar. Der Compiler müsste halt erkennen, dass hier geprüft werden soll, ob der zeiger im Array liegt oder nicht. Ich will damit sagen, wenn der Compiler hier sowieso was wegoptimiert, dann könnte er doch durch etwas Logik hier so optimieren, dass er erkennt, was der Programmierer bezwecken will. Denn dann könnte er eindeutig true oder false liefern.
Vergleichsoperatoren auf Pointer dienen im Allgemeinen dazu, Pointer zu vergleichen.BeRsErKeR hat geschrieben:Ja wenn man das allgemein auf Vergleichsoperatoren bezieht mag das ja stimmen, aber Vergleichsoperatoren auf Pointer dienen doch eigentlich immer zur Feststellung, ob der Pointer in einem bestimmten Bereich liegt (relativ zu einem anderen Pointer/Array).
Nur weil es auf einem PC so ist, muss es noch lange nicht im Allgemeinen auch so sein. Dein Missverständnis beruht wohl darauf, dass du davon ausgehst, dass es immer und überall genau nur einen großen, zusammenhängenden Addressraum geben muss...BeRsErKeR hat geschrieben:Wenn ich also prüfe ob Pointer x im (zusammenhängenden) Bereich y liegt, dann ist es völlig egal wo der liegt. Es ist immer eindeutig möglich festzustellen, ob x im Bereich y liegt oder nicht.
Ok jetzt bin ich verwirrt. So lange du nur Pointer vergleichst, die alle in das selbe Array zeigen, ist das alles natürlich kein Problem und wohldefiniert. Problematisch ist der Vergleich von Pointern, die nicht in das selbe Array zeigen...BeRsErKeR hat geschrieben:Man vergleicht ja relativ mit einem Bereich und nicht absolut. Die Sprachmittel von C/C++ sind hier vielleicht etwas ungeeignet, aber von der Theorie her ist dieser Vergleich absolut unproblematisch, sofern man mit Bereichen prüft, die sequenziell hintereinander liegen (was Arrays ja bekanntlich tun).
Und genau das tun diese Operatoren... ;)BeRsErKeR hat geschrieben:Ich weiß, dass das natürlich dann nicht gilt, wenn man einfach nur auf kleiner oder größer prüft [...]
- TGGC
- Establishment
- Beiträge: 569
- Registriert: 15.05.2009, 18:14
- Benutzertext: Ich _bin_ es.
- Alter Benutzername: TGGC
- Echter Name: Ich _bin_ es.
- Wohnort: Mainz
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Man koennte die Arrays auch auf einem Heap packen, der nach dem Start der Arrays sortiert ist und wuerde dann in amortisierter Laufzeit von log n das passende Array finden. Oh, genau das passiert wenn man new und delete auf die Zeiger aufruft, na so ein Zufall...
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Genau das passiert auf deinem PC...im C++ Standard steht nichts von einem irgendwie sortiertem Heap, das Wort Heap wird dort (in diesem Kontext) nichtmal verwendet und von einem einzelnen, zusammenhängenden Adressraum steht dort auch nichts. Im C++ Standard steht allerdings auch nichts, das einem Compiler verbieten würde, auf einer entsprechenden Plattform entsprechend zu optimieren. Und die ursprüngliche Frage war nunmal, was der C++ Standard dazu sagt und nicht, was ein konkreter Compiler auf einer konkreten Plattform daraus machen wird. Wer das wissen will: Mit dem jeweiligen Compiler ausprobieren. Der Punkt ist, dass solcher Code zwar gültiges C++, aber nicht portabel ist. Ehrlich gesagt, würd ich mir aber ganz andere Gedanken machen, wenn mein Code über Vergleichsoperatoren feststellen müsste, in welches Objekt ein bestimmter Pointer zeigt...TGGC hat geschrieben:Oh, genau das passiert wenn man new und delete auf die Zeiger aufruft, na so ein Zufall...
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Ich versuche ganz im Gegenteil redundante Information zu vermeiden:dot hat geschrieben:Ehrlich gesagt, würd ich mir aber ganz andere Gedanken machen, wenn mein Code über Vergleichsoperatoren feststellen müsste, in welches Objekt ein bestimmter Pointer zeigt...
struct Sample {
char const * toPooledData;
bool is16Bit; //IGITT!
};
…
dispatch(sample.toPooledData, sample.is16Bit ? pcm16Bit : pcm8Bit);
Das verschwendet durch die eklige 9-Byte-Größe und das Padding glatt 40 % Speicher. is16Bit muss initialisiert und gepflegt werden. Meiner Meinung nach ist die Variable überflüssig, weil ich durch den Wert des Zeigers feststellen kann, auf welchen Pool verwiesen wird:
struct Sample {
char const * toPooledData;
};
…
dispatch(sample.toPooledData, pointsToArray(sample.toPooledData, samplePool_16Bit) ? pcm16Bit : pcm8Bit);
verbraucht nur halb so viel Speicher und ich spare mir das Pflegen der bool.
Eine andere Möglichkeit wäre gewesen, 8-Bit-Samples absichtlich an unrunden Adressen abzulegen, und das letzte Bit des Zeigers zu prüfen:
dispatch(sample.toPooledData, (reinterpret_cast<uintptr_t>(sample.toPooledData) % 2) ? pcm8Bit : pcm16Bit);
aber das hätte zusätzliche Komplexität beim Pooling bedeutet.
Normale Programmierer sollten natürlich eine virtuelle Methode benutzen ;-)
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Nein das ist doch völlig egal. Wichtig ist nur, dass der Bereich, mit dem ich Vergleiche, zusammenhängend ist. Wenn z.B. Bereich A auf der GPU liegt und Bereich B irgendwo anders, dann kann ich dennoch eindeutig feststellen, ob Pointer x in Bereich A oder Bereich B liegt. Man darf einen Pointer halt nicht als absolute Adresse ansehen. So viel Logik traue ich einem modernen Compiler schon zu.dot hat geschrieben:Nur weil es auf einem PC so ist, muss es noch lange nicht im Allgemeinen auch so sein. Dein Missverständnis beruht wohl darauf, dass du davon ausgehst, dass es immer und überall genau nur einen großen, zusammenhängenden Addressraum geben muss...BeRsErKeR hat geschrieben:Wenn ich also prüfe ob Pointer x im (zusammenhängenden) Bereich y liegt, dann ist es völlig egal wo der liegt. Es ist immer eindeutig möglich festzustellen, ob x im Bereich y liegt oder nicht.
Ich rede vom Vergleich eines beliebigen Pointers mit einem zusammenhängenden Bereich (z.B. ein Array). Es ist eindeutig feststellbar, ob ein Pointer innerhalb eines solchens Bereiches liegt, wenn man sich von der Vorstellung verabschiedet, dass ein Pointer eine absolute Adresse ist. Wenn das Array auf einem bestimmten Medium liegt und dort einen bestimmten Adressraum belegt, dann kann man auch feststellen, ob der Pointer auf diesem Medium und in diesem Bereich liegt oder nicht.dot hat geschrieben:Ok jetzt bin ich verwirrt. So lange du nur Pointer vergleichst, die alle in das selbe Array zeigen, ist das alles natürlich kein Problem und wohldefiniert. Problematisch ist der Vergleich von Pointern, die nicht in das selbe Array zeigen...BeRsErKeR hat geschrieben:Man vergleicht ja relativ mit einem Bereich und nicht absolut. Die Sprachmittel von C/C++ sind hier vielleicht etwas ungeeignet, aber von der Theorie her ist dieser Vergleich absolut unproblematisch, sofern man mit Bereichen prüft, die sequenziell hintereinander liegen (was Arrays ja bekanntlich tun).
Daher sagte ich ja auch, dass man natürlich nicht global auf kleiner oder größer prüfen kann, aber feststellen, ob sich ein Pointer in einem bestimmten Bereich befindet kann man immer.
Ja daher sagte ich ja, dass die Sprachmittel von C/C++ hier etwas ungeeignet sind. Ein Pointer-Vergleich ist ja nur sinnvoll um festzustellen, wo sich der Pointer gerade befindet. Niemand wird jemals wissen wollen, ob sich ein Pointer absolut vor oder hinter einem bestimmten Speicherbereich befindet, sondern nur ob er sich innerhalb oder außerhalb eines solchen befindet. Daher wäre viel zweckmäßiger dafür Operatoren bereitzustellen oder if-Abfragen (wie die von Krishty) so zu optimieren, dass sie das gewünschte Ergebnis liefern (denn in diesem Fall gibt es immer eine eindeutig Aussage).dot hat geschrieben:Und genau das tun diese Operatoren... ;)BeRsErKeR hat geschrieben:Ich weiß, dass das natürlich dann nicht gilt, wenn man einfach nur auf kleiner oder größer prüft [...]
Ohne Input kein Output.
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Wenn aber pX als fremder Wert hereinkommt, wird der Code stehen gelassen, da die Bedingung nicht konstant gefaltet werden kann.Krishty hat geschrieben:Doch; natürlich wird es das. Es gab mal ein Memo einer US-Behörde, die mit Range Checks gegen Pufferüberläufe sichern wollte, und dann blöd geguckt hat, als GCC die alle durch if(true) ersetzt hat. Clang optimiert undefiniertes Verhalten noch aggressiver weg (insbesondere Nullzeigerprüfungen). Darum sollte man solchen Quelltext unter C auf gar keinen Fall benutzen.
Dies:
if(pX >= foo && pX < foo + 100)
*pX = 0;
wird von modernen C-Optimizern so interpretiert:Ergebnis:
- pX wird mit dem Array foo verglichen.
- Ein Zeiger darf mit anderen Zeigern nur verglichen werden, falls sie auf dasselbe Objekt (in diesem Fall: das Array foo) zeigen.
- Zeigt pX nicht auf ein Element von foo, ist das Programm undefiniert. Dann dürfen wir tun, was wir wollen.
- Wir entscheiden uns, dann das selbe zu tun wie in dem Fall, wo pX tatsächlich auf foo zeigt.
*pX = 0; // if eliminiert
http://fedoraproject.org/ <-- freies Betriebssystem
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
http://launix.de <-- kompetente Firma
In allen Posts ist das imo und das afaik inbegriffen.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Lies es nochmal: Wo pX herkommt ist egal. Da die Sprache nichts anderes erlaubt als ihn mit dem selben Array zu vergleichen worauf er auch tatsächlich zeigt, darf der Compiler annehmen, dass er immer auf das Array zeigt, mit dem er auch verglichen wird. Der Fall, dass der Zeiger nicht zum Array gehört, ist in einem wohldefinierten C-Programm nicht existent.
Re: [C++] Zeiger gegen fremdes Array vergleichen: Erlaubt?
Habe gerade auf stackoverflow folgendes entdeckt:
EDIT: Ok, ich sehe gerade, dass std::less etc. zwar auf jeden Fall ein Ergebnis liefern, aber dass muss ja noch lange nicht bedeuten, dass der Zeiger auch wirklich in einem bestimmten Bereich liegt. Wie die total order nun letztlich aussieht, wird ja nicht definiert.
Damit müsste doch das ursprüngliche Problem standardkonform zu lösen sein, oder?20.3.3/8 in C++03 hat geschrieben:For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield a total order, even if the built-in operators <, >, <=, >= do not.
EDIT: Ok, ich sehe gerade, dass std::less etc. zwar auf jeden Fall ein Ergebnis liefern, aber dass muss ja noch lange nicht bedeuten, dass der Zeiger auch wirklich in einem bestimmten Bereich liegt. Wie die total order nun letztlich aussieht, wird ja nicht definiert.