Gast
#5866917
Hallöchen, was genau hat man mit diesen Zeilen bezweckt? Springt mir nicht direkt ins Auge <^-^>..
1 | |
2 | |
Außer das es im Endeffekt nur den Wert zurückliefert?
|
Anzeige
|
Sinn dieses Codes?!
Gast
#5866917
Hallöchen, was genau hat man mit diesen Zeilen bezweckt? Springt mir nicht direkt ins Auge <^-^>..
Außer das es im Endeffekt nur den Wert zurückliefert?
Gast
#5866932
Dieser hack wird verwendet um: 1) Gut getarnte Sicherheitslücken an verschiedensten Stellen einzubauen 2) Zu verhindern, dass es auf big endian Systemen läuft 3) Den Switch 32->64Bit zu erschweren 4) Undefined behaviour durch Type-Punning einzubauen etc. Also durchaus zahlreiche Anwendungsfälle. :) Das ist eine Konstruktion, um eine Variable irgendeines Typs wie einen 16- oder 32-Bit-unsigned-Int beschreiben zu können. Das setzt voraus, daß sizeof int < sizeof long ist, was z.B. beim AVR der Fall ist. Etwas "portabler" wird das, wenn statt "unsigned int" und "unsigned long" die Typen "uint16_t" und "uin32_t" verwendet werden. Das Konstrukt ist mit Vorsicht zu genießen, das hier beispielsweise geht in die Hose:
Warum? Weil c nur Platz für ein einzelnes Byte zur Verfügung stellt, im
ersten Fall aber zwei und im zweiten sogar vier Bytes beschrieben
werden.
D.h. der Speicher ist dann korrumpiert.
Und je nach Prozessorarchitektur kann das auch zu lustigen
Alignment-Fehlern führen; viele Prozessoren können 16- bzw.
32-Bit-Zugriffe nur an ausgerichteten Adressen ausführen, und werfen
Exceptions, wenn sie unpassende ("misaligned") Adressen verwenden
sollen.
Gast
#5866934
X kann ja auch char sein. Du kannst auf den Element von ein unsigned char[] als unsigned int zugreifen. Zum Beispiel bei Serielle empfangen weißt du das array[5] und array[6] ein Länge ist mit 16 bit. Damit kannst du WORD(array[6]) nehmen und der Compiler versucht den als unsigned int auszulesen. Ggf. ist das schneller als shiften und ||. Das macht was ganz dreckiges. Es castet einen Pointer auf einen anderen Typ, d.h. der Compiler warnt nicht und konvertiert auch nicht. Man kann sich damit z.B. die Hex-Entsprechung eines float Wertes ansehen.
Gast
#5866974
Rufus Τ. F. schrieb: > Das setzt voraus, daß sizeof int < sizeof long ist, was z.B. beim AVR > der Fall ist. Nein. Es ist erstmal egal, wie der User WORD und LONG in seinem Universum interpretiert. Und er kann mit plattformspezifischer Anpassung dieser #defines den Code "Portieren" (wenn man von U.B. oder Endian oder oder absieht. A.S. schrieb: > Nein. Es ist erstmal egal, wie der User WORD und LONG in seinem > Universum interpretiert. Nun, mit einem 32-Bit-Compiler machen beide Macros das gleiche. Mit einem 16-Bit/8-Bit-Compiler aber nicht. Und was bei einem 64-Bit-Compiler rauskommt, kann mal so, mal anders aussehen. Daß die Namen beknackt und irreführend sind (insbesondere "LONG", das mit unsigned long gleichgesetzt wird), ist ein anderes Paar Schuhe. Gerdi schrieb: > Hallöchen, > > was genau hat man mit diesen Zeilen bezweckt? > Springt mir nicht direkt ins Auge <^-^>.. > >
> > Außer das es im Endeffekt nur den Wert zurückliefert? Mit diesen Zeilen spart man vermeintlich Schreibarbeit, um den Preis dass der Code dafür schlechter wird (weil nicht offensichtlich ist was passiert). Wenn man hart auf eine Adresse im Speicher schreiben will, dann sollte man das nicht in einem Makro verstecken. Sooooo oft wird man das nicht brauchen, wenn man vernünftigen Code schreibt. Dass die Namen der Makros nicht besonders intelligent gewählt sind, hat Rufus ja schon richtig beschrieben. DPA schrieb: > Dieser hack wird verwendet um: > 1) Gut getarnte Sicherheitslücken an verschiedensten Stellen einzubauen > 2) Zu verhindern, dass es auf big endian Systemen läuft > 3) Den Switch 32->64Bit zu erschweren > 4) Undefined behaviour durch Type-Punning einzubauen 5) Verletzung der C/C++ Strict Aliasing Regeln 6) Je nach Plattform unaligned Access Rufus Τ. F. schrieb: > Daß die Namen beknackt und irreführend sind (insbesondere "LONG", das > mit unsigned long gleichgesetzt wird), ist ein anderes Paar Schuhe. absolut beknackt Gerdi schrieb: > #define WORD(X) (*((unsigned int *) &(X))) > #define LONG(X) (*((unsigned long *) &(X))) wenn schon denn doch so > #define UWORD(X) (*((unsigned int *) &(X))) > #define ULONG(X) (*((unsigned long *) &(X))) > #define WORD(X) (*((int *) &(X))) > #define LONG(X) (*((long *) &(X))) aber wir hatten damals zu DOS Zeiten > #define (UWORD) (unsigned int) > #define (ULONG) (unsigned long) > #define (WORD) (int) > #define (LONG) (long) geschrieben, wobei mir heute uint8_t uint16_t uint32_t uint64_t int8_t int16_t int32_t int64_t besser gefällt
Gast
#5867251
DPA schrieb: > Dieser hack wird verwendet um: > 1) Gut getarnte Sicherheitslücken an verschiedensten Stellen einzubauen Sicherheitslücken auf nen 8 Bitter???
Gast
#5867271
Nur als interessante Information, sowas wird in der Berechnung der invertierten Wurzel im Quake-III-Quelltext verwendet. https://en.wikipedia.org/wiki/Fast_inverse_square_root Es musste sein, aber die wissen, dass das extrem unschön ist. Trotzdem mag ich diesen Trick. :-) Zitiert von dort:
Woraus für mich die Frage folgt: Wie würde man diesen Algorithmus standard-konform in C++ implementieren können?
Gast
#5867657
Mit std::bit_cast (since C++20) https://en.cppreference.com/w/cpp/numeric/bit_cast
Gast
#5867677
loeti2 schrieb: > Mit std::bit_cast (since C++20) > > https://en.cppreference.com/w/cpp/numeric/bit_cast Kommt das tatsächlich so oft vor, dass das im neuen Standard neu eigeführt wird? Sowas habe ich noch nie gebraucht.
Gast
#5867729
Rufus Τ. F. schrieb: > un, mit einem 32-Bit-Compiler machen beide Macros das gleiche. Genau deshalb solche Makros, mit denen man Plattform-abweichungen behandelt. Wenn Word 16 Bit sein sollen, dann halt short loeti2 schrieb: > Mit std::bit_cast (since C++20) > https://en.cppreference.com/w/cpp/numeric/bit_cast Danke. Also bis auf weiteres geht das ausschließlich mit memcpy. Dussel schrieb: > Kommt das tatsächlich so oft vor, dass das im neuen Standard > neu eigeführt wird? Sowas habe ich noch nie gebraucht. Man braucht es z.B. immer dann, wenn man "Daten unbestimmten Typs" hat, die man je nach Kontext (datenabhängig) unterschiedlich interpretieren muss. Ein Beispiel wären Interpreter, denn die bekommen nur einen Byte- oder Word-Datenstrom und müssen daraus sämtliche Datentypen regenerieren. Wie man das bei JIT-Compilern löst, habe ich mir noch nicht näher angeschaut, aber das ist bestimmt auch spaßig. Man kann alles auch in Software lösen (notfalls, indem man die gesamte Arithmetik in Software nachbaut), aber das ist alles langsam - was man gerade bei Interpretern nicht möchte. Deswegen gibt es auch Konstrukte wie z.B. "computed goto", weil sich damit schneller und halbwegs gut lesbarer Code für diese Fälle bauen lässt. An anderen Stellen sind das furchtbar grausige Hacks... S. R. schrieb: > Type-punning with a union type is considered undefined behavior > in C++ standards. Darüber streiten sich die Gelehrten, die Formulierungen kann man so oder so auslegen. Höchstwahrschinlich ist es implementation defined wie bei C auch, das jeweilige ABI gibt vor wie das Speicherlayout einer Union auszusehen hat. Bei C++ wurde nur vergessen die bei C nachträglich eingefügte Fußnote mit dieser Klarstellung mit reinzuschreiben, das Grundproblem ist aber exakt genauso gelagert. Antwort schreibenBitte melde dich an, um einen Beitrag zu schreiben. |
Anzeige
|