Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel wird die Verwendung der C++-Ausdruckssyntax mit den Windows-Debuggingtools beschrieben.
Der Debugger akzeptiert zwei verschiedene Arten numerischer Ausdrücke: C++-Ausdrücke und Microsoft Macro Assembler (MASM)-Ausdrücke. Jeder dieser Ausdrücke folgt seinen eigenen Syntaxregeln für Eingabe und Ausgabe.
Weitere Informationen dazu, wann jeder Syntaxtyp verwendet wird, finden Sie unter Auswerten von Ausdrücken und dem Befehl "Ausdruck auswerten" .
Der C++-Ausdrucksparser unterstützt alle Formen der C++-Ausdruckssyntax. Die Syntax enthält alle Datentypen, einschließlich Zeiger, Gleitkommazahlen und Arrays sowie alle unären und binären Operatoren C++.
Die Fenster "Überwachung" und " Locals " im Debugger verwenden immer den C++-Ausdrucksauswerter.
Im folgenden Beispiel zeigt der Befehl zum Auswerten des C++-Ausdrucks den Wert des Anweisungszeigerregisters 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
Legen Sie den Ausdrucksauswerter auf C++ fest
Verwenden Sie den .expr choose expression-Evaluator, um den Standardausdruck-Evaluator anzuzeigen und ihn auf 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 Standardauswertungsauswertung geändert wurde, kann der Befehl "Ausdruck auswerten " zum Anzeigen von C++-Ausdrücken verwendet werden. Im folgenden Beispiel wird der Wert des Anweisungszeigerregisters angezeigt.
0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02
Weitere Informationen zum @eip Registerverweis finden Sie unter Registersyntax.
In diesem Beispiel wird der Hexadexwert 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 Pseudo-Register hinzugefügt werden.
Der Ausdrucksauswerter führt automatisch die richtige Umwandlung aus. Tatsächliche Register und Pseudoregister mit ganzzahligen Werten werden in ULONG64 umgewandelt. Alle Adressen werden in PUCHAR umgewandelt, $thread wird in ETHREAD* umgewandelt, $proc wird in EPROCESS* umgewandelt, $teb wird in TEB* umgewandelt, und $peb wird in PEB* umgewandelt.
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 ein Pseudoregister nicht durch einen Zuweisungs- oder Seiteneffekt-Operator ändern. Sie müssen den Befehl "r registers " verwenden, um diese Werte zu ändern.
Im folgenden Beispiel wird das Pseudoregister auf einen Wert von 5 festgelegt und anschließend 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 eine andere Weise an. Um eine hexadezimale ganze Zahl anzugeben, fügen Sie vor der Zahl 0x hinzu. Um eine oktale ganze Zahl anzugeben, fügen Sie vor der Zahl 0 (Null) hinzu.
Der Standarddebugger radix wirkt sich nicht darauf aus, wie Sie C++-Ausdrücke eingeben. Sie können keine binäre Zahl direkt eingeben, es sei denn, Sie verschachteln einen MASM-Ausdruck innerhalb des C++-Ausdrucks.
Sie können einen hexadezimalen 64-Bit-Wert im xxxx'xxxxxx-Format eingeben. Sie können auch den Grabakzent (') weglassen. Beide Formate erzeugen denselben Wert.
Sie können die Suffixe L, 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 des C++-Ausdrucksvaluators 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. Sie müssen beispielsweise 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, sie kann jedoch nicht für die C++-Ausdruckseingabe verwendet werden.
Zeichen und Zeichenfolgen in C++-Ausdrücken
Sie können ein Zeichen eingeben, indem Sie es mit einfachen 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, worauf sich das Symbol bezieht, kann es als eine ganze Zahl, eine Datenstruktur, einen Funktionszeiger oder einen anderen Datentyp interpretiert werden. Ein Syntaxfehler tritt auf, wenn Sie ein Symbol verwenden, das keinem C++-Datentyp entspricht, z. B. einen nicht geänderten Modulnamen, innerhalb eines C++-Ausdrucks.
Sie können ein Graviszeichen (') oder ein Apostroph (') nur in einem Symbolnamen verwenden, wenn Sie vor dem Symbolnamen einen Modulnamen und ein Ausrufezeichen hinzufügen. Wenn Sie die < und > Trennzeichen nach einem Vorlagennamen hinzufügen, können Sie Leerzeichen zwischen diesen Trennzeichen hinzufügen.
Wenn das Symbol möglicherweise 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 aus, und schließen Sie ein Dollarzeichen und ein Ausrufezeichen ($!) vor dem Symbolnamen 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 als ein 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 mit der Verwendung eines Zeigers auf ein Element der referenzierten Struktur angezeigt.
0:000> ?? @$teb->ClientId.UniqueProcess
void * 0x0000059c
Operatoren in C++-Ausdrücken
Sie können Klammern verwenden, um Vorrangregeln außer Kraft zu setzen.
Wenn Sie einen Teil eines C++-Ausdrucks in Klammern setzen und vor dem Ausdruck zwei At-Zeichen (@@) hinzufügen, wird der Ausdruck gemäß MASM-Ausdrucksregeln interpretiert. Sie können kein Leerzeichen zwischen den beiden at Zeichen und der öffnenden Klammer hinzufügen. Der endgültige Wert dieses Ausdrucks wird als ULONG64 Wert an den C++-Ausdrucksauswert übergeben. Sie können den Ausdrucksauswerter auch mithilfe von @@c++( ... ) oder @@masm( ... ) angeben.
Datentypen werden wie gewohnt in der Sprache C++ angegeben. Die Symbole, die Arrays ([ ]), Zeigermitglieder (>), UDT-Mitglieder (.) und Mitglieder von Klassen (::) angeben, werden alle erkannt. Alle arithmetischen Operatoren werden unterstützt, einschließlich Zuordnungs- und Nebeneffektoperatoren. Sie können jedoch die newOperatoren und deletethrow Operatoren nicht verwenden, und Sie können keine Funktion aufrufen.
Zeigerarithmetik wird unterstützt, und Offsets werden korrekt 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 nicht-ganzzahligen Wert verschieben.
Sie können die folgenden Operatoren verwenden. Die Operatoren in jeder Zelle haben Vorrang vor Operatoren in niedrigeren Zellen. Operatoren in derselben Zelle haben die gleiche Rangfolge und werden von links nach rechts analysiert.
Wie bei C++ endet die Ausdrucksauswertung, wenn ihr Wert bekannt ist. Mit diesem Ende können Sie Ausdrücke wie ?? myPtr && *myPtrz. B. effektiv verwenden.
Bezugs- und Typguss
| Bediener | Bedeutung |
|---|---|
| Ausdruck // Kommentar | Alle nachfolgenden Text ignorieren |
| Klasse :: Mitglied | Mitglied der Klasse |
| Klasse ::~Mitglied | Mitglied der Klasse (Destruktor) |
| :: Name | Weltweit |
| Struktur . Feld | Feld in einer Struktur |
| Zeiger ->Feld | Feld in referenzierter Struktur |
| Name [ganze Zahl] | Array-Index |
| Lvalue ++ | Inkrement (nach Auswertung) |
| Lvalue -- | Dekrementierung (nach Auswertung) |
| < dynamic_castTyp>(Wert) | Typecast (immer ausgeführt) |
| static_cast<Typ>(Wert) | Typecast (immer ausgeführt) |
| reinterpret_cast<Typ>(Wert) | Typecast (immer ausgeführt) |
| < const_castTyp>(Wert) | Typecast (immer durchgeführt) |
Wertvorgänge
| Bediener | Bedeutung |
|---|---|
| (Typ) Wert | Typecast (immer ausgeführt) |
| sizeofvalue | Größe des Ausdrucks |
| sizeof( type ) | Größe des Datentyps |
| ++ Lvalue | Inkrementierung (vor Auswertung) |
| -- Lvalue | Dekrementierung (vor Auswertung) |
| ~ Wert | Bit-Komplement |
| ! Wert | Not (Boolean) |
| Wert | Unäres Minus |
| + Wert | Unäres Plus |
| & LValue | Adresse des Datentyps |
| Wert | Dereferenzieren |
| Struktur . Zeiger | Zeiger auf Strukturelement |
| Zeiger -> * Zeiger | Zeiger auf Element der referenzierten Struktur |
Arithmetik
| Bediener | Bedeutung |
|---|---|
| Wertwert | Multiplikation |
| Wert / Wert | Abteilung |
| Wert % Wert | Modul |
| Wert + Wert | Ergänzung |
| Wert - Wert | Subtraktion |
| Wert<<Wert | Bitweise Schiebung nach links |
| Wert>>Wert | Bitweise Verschiebung nach rechts |
| Wert<Wert | Kleiner als (Vergleich) |
| Wert<= Wert | Kleiner oder gleich (Vergleich) |
| Wert>Wert | Größer als (Vergleich) |
| Wert>= Wert | Größer oder gleich (Vergleich) |
| Wert == Wert | Gleichheit (Vergleich) |
| Wert != Wert | Ungleich (Vergleich) |
| Wert und Wert | Bitweise UND |
| Wert ^ Wert | Bitweise XOR (exklusiv ODER) |
| Wert | Wert | Bitweise ODER |
| Wert && Wert | Logisches AND |
| Wert || Wert | Logisches OR |
In den folgenden Beispielen wird davon ausgegangen, dass die Pseudoregister wie dargestellt festgelegt werden.
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
| Bediener | Bedeutung |
|---|---|
| Lvalue = Wert | Zuweisen |
| Lvalue *= Wert | Multiplizieren und Zuweisen |
| Lvalue /= Wert | Dividieren und Zuweisen |
| Lvalue %= Wert | Modulo und Zuweisen |
| Lvalue += Wert | Hinzufügen und Zuweisen |
| Lvalue -= Wert | Subtrahieren und Zuordnen |
| Lvalue<<= Wert | Umschalten nach links und Zuweisen |
| Lvalue>>= Wert | Umschalten nach rechts und Zuweisen |
| LValue &= Wert | UND und zuordnen |
| Lvalue |= Wert | ODER und Zuweisen |
| Lvalue ^= Wert | XOR und Zuweisen |
Auswertung
| Bediener | Bedeutung |
|---|---|
| Wert ? Wert : Wert | Bedingte Auswertung |
| Wert , Wert | Alle Werte auswerten und dann alle außer dem wert ganz rechts verwerfen |
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 dieselben Definitionen wie die Microsoft Windows-Makros mit demselben Namen. Die Windows-Makros werden in Winnt.hdefiniert.
| Makro | Rückgabewert |
|---|---|
| #CONTAINING_RECORD(Adresse, Typ, Feld) | Gibt die Basisadresse einer Instanz einer Struktur zurück, wobei der Typ der Struktur und die Adresse eines Felds innerhalb der Struktur angegeben ist. |
| #FIELD_OFFSET(Typ, Feld) | 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(Typ, Feld) | 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 der Elemente in einem statisch dimensionierten Array zurück. |
| #RTL_SIZEOF_THROUGH_FIELD(Typ, Feld) | Gibt die Größe einer Struktur eines bekannten Typs zurück, bis zu und einschließlich eines angegebenen Felds. |
Dieses Beispiel zeigt die Verwendung des #FIELD_OFFSET Makros zum Berechnen des Byteoffsets auf ein Feld in einer Struktur.
0:000> ?? #FIELD_OFFSET(_PEB, BeingDebugged)
long 0n2
Siehe auch
MASM-Ausdrücke im Vergleich zu C++-Ausdrücken