Forum: Mikrocontroller und Digitale Elektronik Sinn dieses Codes?!


von Gerdi (Gast)


Lesenswert?

Hallöchen,

was genau hat man mit diesen Zeilen bezweckt?
Springt mir nicht direkt ins Auge <^-^>..
1
#define WORD(X)    (*((unsigned int *) &(X)))
2
#define LONG(X)     (*((unsigned long *) &(X)))

Außer das es im Endeffekt nur den Wert zurückliefert?

von DPA (Gast)


Lesenswert?

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. :)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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:
1
char c;
2
3
WORD(c) = 1234;
4
LONG(c) = 0xdeadbeef;

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.

: Bearbeitet durch User
von kyrk.5 (Gast)


Lesenswert?

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 ||.

von Peter D. (peda)


Lesenswert?

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.

von A.S. (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Mark B. (markbrandis)


Lesenswert?

Gerdi schrieb:
> Hallöchen,
>
> was genau hat man mit diesen Zeilen bezweckt?
> Springt mir nicht direkt ins Auge <^-^>..
>
>
1
> #define WORD(X)    (*((unsigned int *) &(X)))
2
> #define LONG(X)     (*((unsigned long *) &(X)))
3
>
>
> 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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

von Joachim B. (jar)


Lesenswert?

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

von Gerdi (Gast)


Lesenswert?

DPA schrieb:
> Dieser hack wird verwendet um:
>  1) Gut getarnte Sicherheitslücken an verschiedensten Stellen einzubauen

Sicherheitslücken auf nen 8 Bitter???

von Dussel (Gast)


Lesenswert?

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. :-)

von S. R. (svenska)


Lesenswert?

Zitiert von dort:
1
In terms of C standards, reinterpreting a floating point value
2
as an integer by dereferencing a casted pointer to it is considered
3
undefined behavior. The proper way to accomplish the fast inverse
4
square root, in a more standard conforming way, is to type-pun
5
floating point values and integers through a union type.
6
Type-punning with a union type is considered undefined behavior
7
in C++ standards.

Woraus für mich die Frage folgt: Wie würde man diesen Algorithmus 
standard-konform in C++ implementieren können?

von loeti2 (Gast)


Lesenswert?

Mit std::bit_cast (since C++20)

https://en.cppreference.com/w/cpp/numeric/bit_cast

von Dussel (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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

von S. R. (svenska)


Lesenswert?

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...

von Bernd K. (prof7bit)


Lesenswert?

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.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.