MAKEWORD - windef.h
-
Guten Morgen
Ich hab mir mal das MAKEWORD-Makro aus der windef Headerdatei rausgesucht und verstehe das noch nicht ganz. Ich denke, dafür fehlen mir ein paar Hintergrunddetails vom Compiler.
Das Makro lautet:
#define MAKEWORD(a,b) ((WORD)(((BYTE)(((DWORD_PTR)(a))&0xFF))|(((WORD)((BYTE)(((DWORD_PTR)(b))&0xFF)))<<8)))
Ich verstehe im gnerellen nicht, wie aus zwei BYTE (BYTE = char, 1Byte) Werten ein neuer WORD Wert entsteht ohne neuen Speicher zu allozieren. Wenn ich die Klammern auflöse, caste ich als erstes meine Variablen a und b (die kann ja theoretisch jeden Datentyp haben) auf einen DWORD_PTR (unsigned long, in meinem Fall 4 Byte). Danach führe ich eine and Operation mit 0xFF durch. Hier ist meine Frage, ist 0xFF ein Byte groß oder wird diese Konstante standardmäßig als Integer erkannt? Wenn nicht, welchen Sinn hat dann dieser Schritt? Danach wird b wieder auf ein BYTE gecastet und danach sofort auf ein WORD (warum nicht gleich auf ein WORD?). Wenn ich die Variable auf ein WORD caste, heißt das ja nicht, dass ich dann die zwei Byte an Speicher habe, um die Bits um 8 Stellen nach links zu verschieben. Gibt es da einen Puffer, der für Bitshifting zuständig ist oder wie kann ich mir das vorstellen? Der Rest des Makros ist für mich klar.
Wenn ich das Makro einfach nur Aufrufe und das Ergebnis einer Variable zuweise, verstehe ich auch die Disassemblieung nicht. Es wird eine call Instruktion aufgerufen. Ich dachte und hatte das aber so verstanden, dass der hinter Teil des Makros 1-zu-1 für den Namen ersetzt werden.
Beziehungsweise wird dann das Ergebnis (hatte im Test das Makro mit MAKEWORD(2, 1) aufgerufen) als Konstante auf den Stack geschoben (wird das Makro schon vorher ausgeführt)?
int main(int argc, char *argv[]) { WORD test = MAKEWORD(2, 1); }
0x004016b0 <+0>: push ebp 0x004016b1 <+1>: mov ebp,esp 0x004016b3 <+3>: and esp,0xfffffff0 0x004016b6 <+6>: sub esp,0x10 0x004016b9 <+9>: call 0x401c50 <__main> 0x004016be <+14>: mov WORD PTR [esp+0xe],0x102 0x004016c5 <+21>: leave 0x004016c6 <+22>: ret
Könnt ihr mir ein Tutorial empfehlen, wo diese "Kleinigkeiten/Details" behandelt werden? In Standard C Büchern/Tutorials findet man sowas nicht.
Ich hoffe, ich hab mich verständlich ausgedrückt und danke schonmal für eure Antworten :).
-
lesbar formatiert:
( (WORD)( ( (BYTE)( ((DWORD_PTR)(a))&0xFF ) ) | ( ( (WORD)( (BYTE)( ((DWORD_PTR)(b))&0xFF ) ) ) << 8 ) ) )
Ich persönlich würde die Casts auf
DWORD_PTR
entfernen, weil das nur Fehler bei der Benutzung des Macros verdecken würde (man könnte es mit beinahe beliebigen, unsinnigen Werten aufrufen ohne eine Warnung zu bekommen):( (WORD)( ( (BYTE)( (a)&0xFF ) ) | ( ( (WORD)( (BYTE)( (b)&0xFF ) ) ) << 8 ) ) )
Der Cast auf
BYTE
tut das gleiche wie&0xFF
: Alle Bits außer die untersten acht auf null setzen. Eins von beiden kann man weglassen. Ich behalte das&
, weil wir damit eine gewisse Typsicherheit gewinnen (funktioniert nur mit Integern):( (WORD)( ( ( (a)&0xFF ) ) | ( ( (WORD)( ( (b)&0xFF ) ) ) << 8 ) ) )
Der Cast zwischendrin nach
WORD
ist unnötig, weil der Shift sowieso seine Argumente nachint
konvertiert. Das steht da also eigentlich:( (WORD)( ( (a)&0xFF ) | ( ( (b)&0xFF ) << 8 ) ) )
Man könnte jetzt auch noch die
&0xFF
weglassen, weil es sowieso keinen Sinn ergibt, das Makro mit Werten >255 aufzurufen. Das Ergebnis wäre so oder so falsch, unabhängig von den unteren acht Bits.Makeword schrieb:
0x004016b0 <+0>: push ebp 0x004016b1 <+1>: mov ebp,esp 0x004016b3 <+3>: and esp,0xfffffff0 0x004016b6 <+6>: sub esp,0x10 0x004016b9 <+9>: call 0x401c50 <__main> 0x004016be <+14>: mov WORD PTR [esp+0xe],0x102 0x004016c5 <+21>: leave 0x004016c6 <+22>: ret
Das ist nicht die
main
, sondern die vom Compiler generierte Funktion, diemain
aufruft.
-
Danke für deine Antwort :). Die Frage, die sich mir natürlich stellt, warum macht man das Makro dann so kompliziert und unnötig lang? Bekommt man darauf Premien?
Ich persönlich würde die Casts auf DWORD_PTR entfernen, weil das nur Fehler bei der Benutzung des Macros verdecken würde (man könnte es mit beinahe beliebigen, unsinnigen Werten aufrufen ohne eine Warnung zu bekommen)
Kannst du kurz beschreiben, welche Fehler du meinst? Meinst du Logik oder Syntaxfehler?
Der Cast auf BYTE tut das gleiche wie &0xFF: Alle Bits außer die untersten acht auf null setzen.
Hmm, wenn ich folgenden Quellcode verwende:
int main(int argc, char *argv[]) { int test = 256; printf("%i\n", test); printf("%i\n", ((BYTE) test)); printf("%i\n", test); }
Bei der dritten Ausgabe kommen wieder die gewüschten 256 heraus (also werden die "vorderen" 24 Bits nicht physikalisch auf 0 gesetzt). Was passiert den genau beim Casten (beim Compiler)? Wenn ich einen int auf char caste, wird dann direkt auf das erste Byte zugegriffen oder greife ich auf eine Kopie zu (also wird beim Casten eine Kopie mit der entsprechenden Größe erstellt?)
Des weiteren habe ich mal sizeof(2); probiert und mir wurde 4 ausgegebn. Daher vermute ich jetzt einfach, dass ganzzahlige konstante Werte vom Compiler als int-Werte interpretiert werden.Der Cast zwischendrin nach WORD ist unnötig, weil der Shift sowieso seine Argumente nach int konvertiert.
Ist das immer so? Wo werden denn die entsprechenden int-Werte gespeichert (Fragen über Fragen ... :D)?
Das ist nicht die main, sondern die vom Compiler generierte Funktion, die main aufruft.
Jo, das weiß ich. Ansonsten würde das eine endlose Rekursion werden. Aber welche Funktion ruft den main auf? Ich hab ja keine angegeben, sondern nur das Makro, was ja keine Funktion in dem Sinne ist
.
Schönen Tag noch
-
Makeword schrieb:
Hmm, wenn ich folgenden Quellcode verwende:
int main(int argc, char *argv[]) { int test = 256; printf("%i\n", test); printf("%i\n", ((BYTE) test)); printf("%i\n", test); }
Bei der dritten Ausgabe kommen wieder die gewüschten 256 heraus (also werden die "vorderen" 24 Bits nicht physikalisch auf 0 gesetzt).
Du änderst test ja auch nicht. Aber probier mal:
int main(int argc, char *argv[]) { int test = 256; printf("%i\n", test); printf("%i\n", ((BYTE) test)); test = (BYTE)test; printf("%i\n", test); }
-
Jo, das ist mir schon klar, dass wenn ich den gecasteten Wert der eig. Variable zuweise, dass der Wert dann auch ohne Cast vorhanden ist. Das ist ja aber beim MAKEWORD - Macro nicht der Fall. Und wenn ich den Wert nicht direkt verändere, was verändere ich denn dann beim Bitshifting?
-
Makeword schrieb:
Und wenn ich den Wert nicht direkt verändere, was verändere ich denn dann beim Bitshifting?
Eine Kopie.
Beim Design von C hat man sich dazu entschieden, viele Operationen auf Kopien arbeiten zu lassen. Das ergibt Sinn, weil man erstens häufig die Originale erhalten möchte und zweitens der Typ des Ergebnisses ein anderer sein kann als der der Eingabe(n).
Das ist so ähnlich wie in der Mathematik, wo
1+1
nicht den Wert von1
auf2
ändert.