C++-Nummern und -Operatoren
In diesem Artikel wird die Verwendung der Syntax von C++-Ausdrücken mit den Windows-Debugtools beschrieben.
Der Debugger akzeptiert zwei verschiedene Arten von numerischen Ausdrücken: C++-Ausdrücke und MasM-Ausdrücke (Microsoft Macro Assembler). Jeder dieser Ausdrücke folgt seinen eigenen Syntaxregeln für Eingabe und Ausgabe.
Weitere Informationen dazu, wann die einzelnen Syntaxtypen verwendet werden, finden Sie unter Auswerten von Ausdrücken und dem Befehl ? ausdruck auswerten .
Der C++-Ausdrucksparser unterstützt alle Formen der C++-Ausdruckssyntax. Die Syntax umfasst alle Datentypen, einschließlich Zeigern, Gleitkommazahlen und Arrays sowie alle unären und binären C++-Operatoren.
In den Fenstern Watch und Locals im Debugger wird immer der C++-Ausdrucksauswerter verwendet.
Im folgenden Beispiel zeigt der Befehl ?? evaluate C++-Ausdruck den Wert des Befehlszeigerregisters an.
0:000> ?? @eip
unsigned int 0x771e1a02
Wir können die C++- sizeof
Funktion verwenden, um die Größe von Strukturen zu bestimmen.
0:000> ?? (sizeof(_TEB))
unsigned int 0x1000
Festlegen des Ausdrucksauswerters auf C++
Verwenden Sie den Ausdrucksauswerter .expr choose , um den Standardauswertungsauswerter anzuzeigen und in C++ zu ändern.
0:000> .expr
Current expression evaluator: MASM - Microsoft Assembler expressions
0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions
Nachdem der Standardauswertungsauswerter geändert wurde, kann der Befehl Ausdruck auswerten verwendet werden, um C++-Ausdrücke anzuzeigen. Im folgenden Beispiel wird der Wert des Befehlszeigerregisters angezeigt.
0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02
Weitere Informationen zur @eip
Registerreferenz finden Sie unter Registersyntax.
In diesem Beispiel wird der Hexwert von 0xD dem eip-Register hinzugefügt.
0:000> ? @eip + 0xD
Evaluate expression: 1998461455 = 771e1a0f
Register und Pseudoregister in C++-Ausdrücken
Sie können Register und Pseudoregister in C++-Ausdrücken verwenden. Das @-Zeichen muss vor dem Register oder Pseudoregister hinzugefügt werden.
Der Ausdrucksauswerter führt automatisch die richtige Umwandlung aus. Tatsächliche Register und Pseudoregister mit ganzzahligem Wert werden in ULONG64
umgewandelt. Alle Adressen werden in PUCHAR
umgewandelt, $thread
ist in ETHREAD*
, $proc
ist in umgewandelt, $teb
wird in EPROCESS*
umgewandelt, wird in TEB*
umgewandelt und $peb
in umgewandeltPEB*
.
In diesem Beispiel wird der TEB angezeigt.
0:000> ?? @$teb
struct _TEB * 0x004ec000
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : 0x004ec02c Void
+0x030 ProcessEnvironmentBlock : 0x004e9000 _PEB
+0x034 LastErrorValue : 0xbb
+0x038 CountOfOwnedCriticalSections : 0
Sie können ein Register oder pseudoregister nicht durch einen Zuweisungs- oder Nebeneffektoperator ändern. Sie müssen den Befehl r registers verwenden, um diese Werte zu ändern.
Im folgenden Beispiel wird das Pseudoregister auf den Wert 5 festgelegt und dann angezeigt.
0:000> r $t0 = 5
0:000> ?? @$t0
unsigned int64 5
Weitere Informationen zu Registern und Pseudoregistern finden Sie unter Registersyntax und Pseudoregistersyntax.
Zahlen in C++-Ausdrücken
Zahlen in C++-Ausdrücken werden als Dezimalzahlen interpretiert, es sei denn, Sie geben sie auf andere Weise an. Um eine hexadezimale ganze Zahl anzugeben, fügen Sie 0x vor der Zahl hinzu. Um eine oktale ganze Zahl anzugeben, fügen Sie 0 (null) vor der Zahl hinzu.
Der Standarddebugger radix wirkt sich nicht auf die Eingabe von C++-Ausdrücken aus. Sie können keine binärzahl direkt eingeben, außer durch Schachteln eines MASM-Ausdrucks innerhalb des C++-Ausdrucks.
Sie können einen hexadezimalen 64-Bit-Wert im Format xxxxxxxx'xxxxxxxx eingeben. Sie können auch den Grabakzent (') weglassen. Beide Formate erzeugen den gleichen Wert.
Sie können die L
Suffixe , U
und I64
mit ganzzahligen Werten verwenden. Die tatsächliche Größe der erstellten Zahl hängt vom Suffix und der von Ihnen eingegebenen Zahl ab. Weitere Informationen zu dieser Interpretation finden Sie in einer C++-Sprachreferenz.
Die Ausgabe der C++-Ausdrucksauswertung behält den Datentyp bei, den die C++-Ausdrucksregeln angeben. Wenn Sie diesen Ausdruck jedoch als Argument für einen Befehl verwenden, wird immer eine Umwandlung vorgenommen. Beispielsweise müssen Sie keine ganzzahligen Werte in Zeiger umwandeln, wenn sie als Adressen in Befehlsargumenten verwendet werden. Wenn der Wert des Ausdrucks nicht gültig in eine ganze Zahl oder einen Zeiger umgewandelt werden kann, tritt ein Syntaxfehler auf.
Sie können das 0n
Präfix (dezimal) für eine Ausgabe verwenden, aber nicht für die Eingabe von C++-Ausdrücken.
Zeichen und Zeichenfolgen in C++-Ausdrücken
Sie können ein Zeichen eingeben, indem Sie es mit einzelnen Anführungszeichen (') umgeben. Die Standardmäßigen C++-Escapezeichen sind zulässig.
Sie können Zeichenfolgenliterale eingeben, indem Sie sie mit doppelten Anführungszeichen (") umgeben. Sie können "\" als Escapesequenz innerhalb einer solchen Zeichenfolge verwenden. Zeichenfolgen haben jedoch keine Bedeutung für den Ausdrucksauswerter.
Symbole in C++-Ausdrücken
In einem C++-Ausdruck wird jedes Symbol entsprechend seinem Typ interpretiert. Je nachdem, auf was sich das Symbol bezieht, kann es als ganze Zahl, eine Datenstruktur, einen Funktionszeiger oder einen anderen Datentyp interpretiert werden. Ein Syntaxfehler tritt auf, wenn Sie ein Symbol verwenden, das nicht einem C++-Datentyp entspricht, z. B. einem nicht geänderten Modulnamen, innerhalb eines C++-Ausdrucks.
Sie können einen Grabakzent (') oder einen Apostroph (') in einem Symbolnamen nur verwenden, wenn Sie einen Modulnamen und ein Ausrufezeichen vor dem Symbolnamen hinzufügen. Wenn Sie die < Trennzeichen und > nach einem Vorlagennamen hinzufügen, können Sie Leerzeichen zwischen diesen Trennzeichen hinzufügen.
Wenn das Symbol mehrdeutig ist, können Sie einen Modulnamen und ein Ausrufezeichen (!) oder nur ein Ausrufezeichen vor dem Symbol hinzufügen. Um anzugeben, dass ein Symbol lokal sein soll, lassen Sie den Modulnamen weg, und fügen Sie vor dem Symbolnamen ein Dollarzeichen und ein Ausrufezeichen ($!) ein. Weitere Informationen zur Symbolerkennung finden Sie unter Symbolsyntax und Symbolabgleich.
Strukturen in C++-Ausdrücken
Der C++-Ausdrucksauswerter wandelt Pseudoregister in die entsprechenden Typen um. Beispiel $teb
: wird in umgewandelt TEB*
.
0:000> ?? @$teb
struct _TEB * 0x004ec000
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : 0x004ec02c Void
+0x030 ProcessEnvironmentBlock : 0x004e9000 _PEB
+0x034 LastErrorValue : 0xbb
+0x038 CountOfOwnedCriticalSections : 0
Im folgenden Beispiel wird die Prozess-ID in der TEB-Struktur angezeigt, die die Verwendung eines Zeigers auf ein Element der Referenzstruktur anzeigt.
0:000> ?? @$teb->ClientId.UniqueProcess
void * 0x0000059c
Operatoren in C++-Ausdrücken
Sie können Klammern verwenden, um Rangfolgeregeln außer Kraft zu setzen.
Wenn Sie einen Teil eines C++-Ausdrucks in Klammern einschließen und vor dem Ausdruck zwei Zeichen (@@) hinzufügen, wird der Ausdruck gemäß MASM-Ausdrucksregeln interpretiert. Zwischen den beiden Zeichen und der öffnenden Klammer kann kein Leerzeichen hinzugefügt werden. Der letzte Wert dieses Ausdrucks wird als ULONG64 Wert an den C++-Ausdrucksauswerter übergeben. Sie können den Ausdrucksauswerter auch mit @@c++( ... )
oder @@masm( ... )
angeben.
Datentypen werden in der Sprache C++ wie üblich angegeben. Die Symbole, die Arrays ([ ]), Zeigermember (-), UDT-Member> (.) und Member von Klassen (::) angeben, werden alle erkannt. Alle arithmetischen Operatoren werden unterstützt, einschließlich Zuweisungs- und Nebeneffektoperatoren. Sie können jedoch nicht die new
Operatoren , delete
und verwenden, und throw
Sie können auch keine Funktion aufrufen.
Zeigerarithmetik wird unterstützt, und Offsets werden ordnungsgemäß skaliert. Beachten Sie, dass Sie einem Funktionszeiger keinen Offset hinzufügen können. Wenn Sie einem Funktionszeiger einen Offset hinzufügen müssen, wandeln Sie den Offset zuerst in einen Zeichenzeiger um.
Wie in C++ tritt bei Verwendung von Operatoren mit ungültigen Datentypen ein Syntaxfehler auf. Der C++-Ausdrucksparser des Debuggers verwendet etwas entspanntere Regeln als die meisten C++-Compiler, aber alle wichtigen Regeln werden erzwungen. Sie können z. B. keinen ganzzahligen Wert verschieben.
Sie können die folgenden Operatoren verwenden. Die Operatoren in jeder Zelle haben Vorrang vor Operatoren in unteren Zellen. Operatoren in derselben Zelle haben dieselbe Priorität und werden von links nach rechts analysiert.
Wie bei C++ endet die Ausdrucksauswertung, wenn ihr Wert bekannt ist. Diese Endung ermöglicht es Ihnen, Ausdrücke wie ?? myPtr && *myPtr
effektiv zu verwenden.
Verweis- und Typverwandlung
Operator | Bedeutung |
---|---|
Ausdruck // Kommentar | Ignorieren des gesamten nachfolgenden Texts |
Klasse :: Member | Member der -Klasse |
Klasse ::~Member | Member der -Klasse (Destruktor) |
:: Name | Global |
Struktur . Feld | Feld in einer Struktur |
Zeiger ->Feld | Feld in referenzierter Struktur |
Name [integer] | Array-Subscript |
Lvalue ++ | Inkrement (nach auswertung) |
Lvalue -- | Dekrement (nach auswertung) |
Dynamic_cast<Typ>(Wert) | Typecast (immer ausgeführt) |
Static_cast<Typ>(Wert) | Typecast (immer ausgeführt) |
Reinterpret_cast<Typ>(Wert) | Typecast (immer ausgeführt) |
Const_cast<Typ>(Wert) | Typecast (immer ausgeführt) |
Wertvorgänge
Operator | Bedeutung |
---|---|
(Typ) Wert | Typecast (immer ausgeführt) |
sizeofvalue | Größe des Ausdrucks |
sizeof( type ) | Größe des Datentyps |
++ Lvalue | Inkrement (vor der Auswertung) |
-- Lvalue | Dekrement (vor der Auswertung) |
~ Value | Bitkomplement |
! Wert | Nicht (boolescher Wert) |
Wert | Unäres Minus |
+ Value | Unäres Plus |
& LValue | Adresse des Datentyps |
Wert | Dereference |
Struktur . Zeiger | Zeiger auf das Element der Struktur |
Zeiger –> * Zeiger | Zeiger auf das Element der Referenzstruktur |
Arithmetik
Operator | Bedeutung |
---|---|
Wertwert | Multiplikation |
Wert / Wert | Division |
Wert % Wert | Modulo |
Wert + Wert | Addition |
Wert - Wert | Subtraktion |
Wert<<Wert | Bitweise Verschiebung nach links |
Wert>>Wert | Bitweise Verschiebung nach rechts |
Wert<Wert | Kleiner als (Vergleich) |
Wert<= Wert | Kleiner als oder gleich (Vergleich) |
Wert>Wert | Größer als (Vergleich) |
Wert>= Wert | Größer als oder gleich (Vergleich) |
Wert == Wert | Gleich (Vergleich) |
Wert != Wert | Ungleich (Vergleich) |
Wert & Wert | Bitweises AND |
Wert ^ Wert | Bitweiser XOR (exklusives OR) |
Wert | Wert | Bitweises OR |
Wert && Wert | Logisches AND |
Wert || Wert | Logisches OR |
In den folgenden Beispielen wird davon ausgegangen, dass die Pseudoregister wie gezeigt festgelegt sind.
0:000> r $t0 = 0
0:000> r $t1 = 1
0:000> r $t2 = 2
0:000> ?? @$t1 + @$t2
unsigned int64 3
0:000> ?? @$t2/@$t1
unsigned int64 2
0:000> ?? @$t2|@$t1
unsigned int64 3
Zuweisung
Operator | Bedeutung |
---|---|
Lvalue = Wert | Zuweisen |
Lvalue *= Wert | Multiplizieren und Zuweisen |
Lvalue /= Wert | Division und Zuweisung |
Lvalue %= Wert | Modulo und Zuweisung |
Lvalue += Wert | Addition und Zuweisung |
Lvalue -= Wert | Subtraktion und Zuweisung |
Lvalue<<= Wert | Links verschieben und zuweisen |
Lvalue>>= Wert | Rechts verschieben und zuweisen |
LValue &= Value | AND und zuweisen |
Lvalue |= Wert | OR und Zuweisen |
Lvalue ^= Wert | XOR und Zuweisen |
Auswertung
Operator | Bedeutung |
---|---|
Wert ? Wert : Wert | Bedingte Auswertung |
Wert , Wert | Werten Sie alle Werte aus, und verwerfen Sie dann alle außer dem ganz rechtssten Wert. |
Makros in C++-Ausdrücken
Sie können Makros in C++-Ausdrücken verwenden. Sie müssen vor den Makros ein Nummernzeichen (#) hinzufügen.
Sie können die folgenden Makros verwenden. Diese Makros haben die gleichen Definitionen wie die Microsoft Windows-Makros mit demselben Namen. Die Windows-Makros sind in Winnt.h
definiert.
Makro | Rückgabewert |
---|---|
#CONTAINING_RECORD(Adresse, Typ, Feld) | Gibt die Basisadresse eines instance einer Struktur zurück, wobei der Typ der Struktur und die Adresse eines Felds innerhalb der Struktur angegeben ist. |
#FIELD_OFFSET(Type, Field) | Gibt den Byteoffset eines benannten Felds in einem bekannten Strukturtyp zurück. |
#RTL_CONTAINS_FIELD(Struktur, Größe, Feld) | Gibt an, ob die angegebene Bytegröße das gewünschte Feld enthält. |
#RTL_FIELD_SIZE(Type, Field) | Gibt die Größe eines Felds in einer Struktur des bekannten Typs zurück, ohne dass der Typ des Felds erforderlich ist. |
#RTL_NUMBER_OF(Array) | Gibt die Anzahl von Elementen in einem Array mit statischer Größe zurück. |
#RTL_SIZEOF_THROUGH_FIELD(Typ, Feld) | Gibt die Größe einer Struktur des bekannten Typs bis hin zu und einschließlich eines angegebenen Felds zurück. |
In diesem Beispiel wird die Verwendung des Makros #FIELD_OFFSET
zum Berechnen des Byteoffsets auf ein Feld in einer Struktur veranschaulicht.
0:000> ?? #FIELD_OFFSET(_PEB, BeingDebugged)
long 0n2
Weitere Informationen
MASM-Ausdrücke im Vergleich zu C++-Ausdrücken
?? Evaluieren eines C++-Ausdrucks
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für