evil pointer level hacking
-
Hallo!
Ich habe jetzt desöfteren sowas gesehen:i = *reinterpret_cast<long *>( &x );
So, nun würde mich genau interessieren, was so etwas bewirkt, und wie man es "nennt".. Also, was steht nun in i und was kann man damit alles anstellen.
Ich habe schon in einem anderen Thread gelesen, dass es undefiniertes Verhalten liefert, aber trotzdem interessiert es mich noch. Manche raten mir auch davon ab, daher interessiert es mich, warum.
In dem Thread sagte volkard auch folgendes:"darf ich annehmen, daß ++*reinterpret_cast<long*>(this); und andere zugriffe auf private-sachen der basisklasse strikt verboten sind?"
Kann ich nun mit einer class Derived : Base auf die private Elemente von Base zugreifen, oder wie?
"Klärt mich auf"Gruß Philipp
P.S:
Das erste Codebeispiel stammt aus id source, findet sich u.a. in quake 1, 2, 3 und doom3, hier nochmal die komplette Funktion:ID_INLINE float idMath::RSqrt( float x ) { long i; float y, r; y = x * 0.5f; i = *reinterpret_cast<long *>( &x ); i = 0x5f3759df - ( i >> 1 ); r = *reinterpret_cast<float *>( &i ); r = r * ( 1.5f - r * r * y ); return r; }
-
Brutus schrieb:
i = *reinterpret_cast<long *>( &x );
Ein Pointer wird in einen long-Pointer umgewandelt und i zugewiesen.
Ich habe schon in einem anderen Thread gelesen, dass es undefiniertes Verhalten liefert
Nein, illegal ist es, so in einer Klasse zu inkrementieren wie volkard es gemacht hat.
-
Hallo Michael E.!
Geht es evtl. noch etwas genauer? Ich habe ja auch die Funktion RSqrt() gepostet, und ich frage mich, wozu braucht man dieses "illegale Zeugs" (sofern es dort illegal ist), wäre es nicht auch auf andere Weiße (legalere?) möglich gewesen, eine RSqrt Funktion zu schreiben, also ebenfalls ohne Funktion aus <cmath>, ich mein', immerhin funktioniert's ja.
Ich danke schonmal allen die hier (hoffentlich) noch antworten.Gruß Philipp
-
Brutus schrieb:
Hallo!
Ich habe jetzt desöfteren sowas gesehen:i = *reinterpret_cast<long *>( &x );
traurig, gell? nee, gar nicht. traurig für mich, weil ich jetzt wieder total vel schreiben muss.
So, nun würde mich genau interessieren, was so etwas bewirkt, und wie man es "nennt"..
es bewirkt das hier:
//ich nehme an, x ist ein float float* pf=&x; //adresse von x merken long* pi=pf; //fast, geht so nicht. also eigentlich schon, weil die beiden //bloss zeiger sind. aber der compiler sagt aua, weil er meint, daß das da //bestimmt ein tippfehler ist //also zwingen wir ihn long* pi=reinterpret_cast<long*>(pf);//mit der reinterpret_cast-peitsche hält //er endlich die klappe und macht's. i=*pi;
wenn man das ein wenig zusammenfasst, kommt obiger ausdruck raus. man nimmt also das bitmuster, was wo den float da darstellt und tut so, als sei es ein int.
Also, was steht nun in i und was kann man damit alles anstellen.
was völlig wirres. anstellen damit können nur gurus was. naja, man kann fein an die bits kommen. wenn man der meinung ist, daß der mathematische copro bei einer aktion echt zu doof ist, kann man selber hand anlegen.
war es nicht so, daß das erste bit im float einfach das vorzeichen angibt?
fall dem so ist, dannvoid negate(float& f){ *reinterpret_cast<long*>(&f)^=(1<<31); }
habs gleich zusammengeschrieben und nicht mehr erklärt. wirst schon drauf kommen, was ich meine.
Ich habe schon in einem anderen Thread gelesen, dass es undefiniertes Verhalten liefert, aber trotzdem interessiert es mich noch.
reinterpret_cast liefert eh undefiniertes verhalten. das da kann doch zum bleistift nur klappen, wenn ein long und ein float genausoviele bits haben.
Manche raten mir auch davon ab, daher interessiert es mich, warum.
weil es undefiniertes verhalten erzeugt. man muss hier abwägen. sind die 0,5% performancegewinn das risiko wert, daß es auf dem nächsten compiler nicht mehr geht? also wenn man ne doom-engine baut, die eh sicher nach ein paar jahren veraltet ist, braucht man nicht an die nächste oder übernächste compilergeneration denken. und da ist speed voll wichtig. macht man aber ne finanzbuchhaltungssoftware, soll die schon mal ein paar jährchen mehr halten und speed ist fast irrelevant. also wird der gamez-coder sowas machen und der finaz-coder nicht.
"darf ich annehmen, daß ++*reinterpret_cast<long*>(this); und andere zugriffe auf private-sachen der basisklasse strikt verboten sind?"
die frage war gemeint als "darf man cheaten". es war überhaupt keine frage, ob das undefiniert ist. es ist undefiniert. um genau zu sein, würde es auf allen compilern, die ich kenne, klappen, wenn die klasse keine virtuelle methode hat. aber die in dem beispiel hatte eine. dann würde es afair auf gcc klappen aber auf msvc nicht.
Kann ich nun mit einer class Derived : Base auf die private Elemente von Base zugreifen, oder wie?
kannste nicht auf legalem weg. mit undefinierten hacks gehts, aber keiner garantiert, daß der compiler das frisst. es ist nicht standard-c++.
-
Um das mit den hacks zusammenzufassen:
class foo { private: int a,b; }; void function(const foo& f) { int* pf=reinterpret_cast<int*>(& f); std::cout<<*pf<<std::endl; //theoretisch ham wir jetzt ein private-member ausgegeben (a) std::cout<<*(pf+1)<<std::endl; //nochmal (b) *pf=4; //was jetzt? Wir ändern einen private wert eines Objektes, dass const übergeben wurde, na wenn das keine Probleme macht... pf[1]=5; //dito, jetzt würde b verändert };
Das könnte alles klappen, natürlich darf dir jeder kompiler auch murks draus machen.
Aber wenn du z.B. virtuelle Methoden hast, dann muss ja irgendwo der vptr sein. Könnte halt passieren, das du den erwischst.
Zusammengefasst: sowas macht net viel sinn. Der, der sich die Zugriffsbeschränkungen ausgedacht hat wird schon nicht ganz blöd sein. Der einzige Fall, bei dem ich mir vorstellen könnte, dass sowas sinn macht, sind vielleicht bibliotheken mit binärlizenzen...
-
Brutus schrieb:
Hallo!
Ich habe jetzt desöfteren sowas gesehen:i = *reinterpret_cast<long *>( &x );
kann man gewaltig abkürzen und sieht nicht so kryptisch aus...
i = *(long*)&x;
-
net schrieb:
kann man gewaltig abkürzen und sieht nicht so kryptisch aus...
i = *(long*)&x;
Laut Stroustrup hat es aber einen Grund, dass die C++-konforme Lösung so hässlich aussieht: Ganz einfach, damit man sie vermeidet. Natürlich nicht, um dann die typen-unsicheren Casts aus C zu verwenden, sondern um sie ganz zu vermeiden.
-
Konrad Rudolph schrieb:
Laut Stroustrup hat es aber einen Grund, dass die C++-konforme Lösung so hässlich aussieht: Ganz einfach, damit man sie vermeidet.
er hat halt humor
hätte er das ernst gemeint, dann hätt er keinen 'reinterpret_cast' eingebaut und c-style casts aus c++ gestrichen
-
net schrieb:
Konrad Rudolph schrieb:
Laut Stroustrup hat es aber einen Grund, dass die C++-konforme Lösung so hässlich aussieht: Ganz einfach, damit man sie vermeidet.
er hat halt humor
hätte er das ernst gemeint, dann hätt er keinen 'reinterpret_cast' eingebautMan /braucht/ es in einigen hardwarenahen Situationen.
und c-style casts aus c++ gestrichen
Ähm, der Witz an C++ ist doch die Kompatibilität zu C.
Nene, ich denke schon, dass er das wörtlich so gemeint hat wie er es geschrieben hat.
-
Laut Meyers wurden die neuen casts auch eingeführt, weil sie Programm und Mensch beim flüschtigen hinsehen mit einem Konstruktor verwechseln könnten. Und reinterpret_cast<long*>(&foo) zeigt doch viel deutlicher was passiert als (long*)&foo.