Numeri e operatori C++
Questo articolo descrive l'uso della sintassi delle espressioni C++ con gli strumenti di debug di Windows.
Il debugger accetta due tipi diversi di espressioni numeriche: espressioni C++ ed espressioni MaSM (Microsoft Macro Assembler). Ognuna di queste espressioni segue le proprie regole di sintassi per l'input e l'output.
Per altre informazioni sull'uso di ogni tipo di sintassi, vedere Valutazione delle espressioni e del comando ? evaluate expression .
Il parser di espressioni C++ supporta tutte le forme di sintassi delle espressioni C++. La sintassi include tutti i tipi di dati, inclusi puntatori, numeri a virgola mobile e matrici e tutti gli operatori unari e binari C++.
Le finestre Espressioni di controllo e Variabili locali nel debugger usano sempre l'analizzatore di espressioni C++.
Nell'esempio seguente, il comando di espressione ?? evaluate C++ visualizza il valore del registro dei puntatori alle istruzioni.
0:000> ?? @eip
unsigned int 0x771e1a02
È possibile usare la funzione C++ sizeof
per determinare le dimensioni delle strutture.
0:000> ?? (sizeof(_TEB))
unsigned int 0x1000
Impostare l'analizzatore di espressioni su C++
Usare l'analizzatore di espressioni con estensione expr choose per visualizzare l'analizzatore di espressioni predefinito e modificarlo in C++.
0:000> .expr
Current expression evaluator: MASM - Microsoft Assembler expressions
0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions
Dopo la modifica dell'analizzatore di espressioni predefinito, è possibile usare il comando ? evaluate expression per visualizzare le espressioni C++. Nell'esempio seguente viene visualizzato il valore del registro del puntatore all'istruzione.
0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02
Per altre informazioni sul riferimento al registro, vedere Registrare la @eip
sintassi.
In questo esempio, il valore esadecimale di 0xD viene aggiunto al registro eip.
0:000> ? @eip + 0xD
Evaluate expression: 1998461455 = 771e1a0f
Registra e pseudoregistri nelle espressioni C++
È possibile usare registri e pseudoregistri nelle espressioni C++. Il segno @ deve essere aggiunto prima del registro o pseudoregistro.
L'analizzatore di espressioni esegue automaticamente il cast corretto. I registri effettivi e gli pseudoregistri di valori interi vengono gettato in ULONG64
. Viene eseguito il cast di tutti gli indirizzi a PUCHAR
, viene eseguito il cast a ETHREAD*
, $proc
viene eseguito il cast a EPROCESS*
TEB*
$teb
e $peb
viene eseguito il cast a .PEB*
$thread
In questo esempio viene visualizzato il 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
Non è possibile modificare un registro o uno pseudoregistrato da un operatore di assegnazione o effetto collaterale. Per modificare questi valori, è necessario usare il comando r registers .
Nell'esempio seguente lo pseudoregistro viene impostato su un valore pari a 5 e quindi viene visualizzato.
0:000> r $t0 = 5
0:000> ?? @$t0
unsigned int64 5
Per altre informazioni sui registri e sugli pseudoregistri, vedere sintassi register e pseudo-register.
Numeri nelle espressioni C++
I numeri nelle espressioni C++ vengono interpretati come numeri decimali, a meno che non vengano specificati in un altro modo. Per specificare un numero intero esadecimale, aggiungere 0x prima del numero. Per specificare un numero intero ottale, aggiungere 0 (zero) prima del numero.
Il radix del debugger predefinito non influisce sul modo in cui si immettono espressioni C++. Non è possibile immettere direttamente un numero binario, tranne annidando un'espressione MASM all'interno dell'espressione C++.
È possibile immettere un valore esadecimale a 64 bit nel formato xxxxxxxx'xxxxxxxx. È anche possibile omettere l'accento grave ('). Entrambi i formati producono lo stesso valore.
È possibile usare i L
suffissi , U
e I64
con valori integer. Le dimensioni effettive del numero creato dipendono dal suffisso e dal numero immesso. Per altre informazioni su questa interpretazione, vedere informazioni di riferimento sul linguaggio C++.
L'output dell'analizzatore di espressioni C++ mantiene il tipo di dati specificato dalle regole dell'espressione C++. Tuttavia, se si usa questa espressione come argomento per un comando, viene sempre eseguito un cast. Ad esempio, non è necessario eseguire il cast dei valori integer ai puntatori quando vengono usati come indirizzi negli argomenti dei comandi. Se il valore dell'espressione non può essere eseguito in modo valido a un numero intero o a un puntatore, si verifica un errore di sintassi.
È possibile usare il 0n
prefisso (decimale) per alcuni output, ma non è possibile usarlo per l'input dell'espressione C++.
Caratteri e stringhe nelle espressioni C++
È possibile immettere un carattere racchiudendolo tra virgolette singole ('). I caratteri di escape C++ standard sono consentiti.
È possibile immettere valori letterali stringa racchiudendoli tra virgolette doppie ("). È possibile usare \" come sequenza di escape all'interno di una stringa di questo tipo. Tuttavia, le stringhe non hanno alcun significato per l'analizzatore di espressioni.
Simboli nelle espressioni C++
In un'espressione C++ ogni simbolo viene interpretato in base al tipo. A seconda del simbolo a cui fa riferimento, può essere interpretato come un numero intero, una struttura di dati, un puntatore a funzione o qualsiasi altro tipo di dati. Si verifica un errore di sintassi se si usa un simbolo che non corrisponde a un tipo di dati C++, ad esempio un nome di modulo non modificato, all'interno di un'espressione C++.
È possibile utilizzare un accento grave (') o un apostrofo (') in un nome di simbolo solo se si aggiunge un nome di modulo e un punto esclamativo prima del nome del simbolo. Quando si aggiungono i < delimitatori e > dopo un nome di modello, è possibile aggiungere spazi tra questi delimitatori.
Se il simbolo potrebbe essere ambiguo, è possibile aggiungere un nome di modulo e un punto esclamativo (!) o solo un punto esclamativo prima del simbolo. Per specificare che un simbolo deve essere locale, omettere il nome del modulo e includere un segno di dollaro e un punto esclamativo ($!) prima del nome del simbolo. Per altre informazioni sul riconoscimento dei simboli, vedere Sintassi dei simboli e corrispondenza dei simboli.
Strutture nelle espressioni C++
L'analizzatore di espressioni C++ esegue il cast di pseudoregistri ai tipi appropriati. Ad esempio, $teb
viene eseguito il cast come .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
Nell'esempio seguente viene visualizzato l'ID processo nella struttura TEB che mostra l'uso di un puntatore a un membro della struttura a cui viene fatto riferimento.
0:000> ?? @$teb->ClientId.UniqueProcess
void * 0x0000059c
Operatori nelle espressioni C++
È possibile usare le parentesi per sostituire le regole di precedenza.
Se si racchiude una parte di un'espressione C++ tra parentesi e si aggiungono due segni ( @@) prima dell'espressione, l'espressione viene interpretata in base alle regole dell'espressione MASM. Non è possibile aggiungere uno spazio tra i due segni e la parentesi di apertura. Il valore finale di questa espressione viene passato all'analizzatore di espressioni C++ come valore ULONG64. È anche possibile specificare l'analizzatore di espressioni usando @@c++( ... )
o @@masm( ... )
.
I tipi di dati sono indicati come di consueto nel linguaggio C++. I simboli che indicano matrici ([ ]), i membri del puntatore (->), i membri del tipo definito dall'utente (.) e i membri delle classi (::) sono tutti riconosciuti. Tutti gli operatori aritmetici sono supportati, inclusi gli operatori di assegnazione e effetto collaterale. Tuttavia, non è possibile usare gli new
operatori , delete
e throw
e non è possibile chiamare effettivamente una funzione.
L'aritmetica del puntatore è supportata e gli offset vengono ridimensionati correttamente. Si noti che non è possibile aggiungere un offset a un puntatore a funzione. Se è necessario aggiungere un offset a un puntatore a funzione, eseguire prima il cast dell'offset su un puntatore a caratteri.
Come in C++, se si usano operatori con tipi di dati non validi, si verifica un errore di sintassi. Il parser di espressioni C++ del debugger usa regole leggermente più rilassate rispetto alla maggior parte dei compilatori C++, ma vengono applicate tutte le regole principali. Ad esempio, non è possibile spostare un valore non intero.
È possibile usare gli operatori seguenti. Gli operatori in ogni cella hanno la precedenza sugli operatori nelle celle inferiori. Gli operatori nella stessa cella hanno la stessa precedenza e vengono analizzati da sinistra a destra.
Come per C++, la valutazione dell'espressione termina quando il valore è noto. Questa terminazioni consente di usare in modo efficace espressioni come ?? myPtr && *myPtr
.
Cast di riferimenti e tipi
Operatore | significato |
---|---|
Commento espressione // | Ignora tutto il testo successivo |
Classe :: Membro | Membro della classe |
Classe ::~Member | Membro della classe (distruttore) |
:: Nome | Generale |
Struttura. Campo | Campo in una struttura |
Puntatore ->Campo | Campo nella struttura a cui si fa riferimento |
Nome [integer] | Indice inferiore di matrice |
LValue ++ | Incremento (dopo la valutazione) |
LValue -- | Decremento (dopo la valutazione) |
<dynamic_cast type>(Value) | Typecast (sempre eseguito) |
<static_cast type>(Value) | Typecast (sempre eseguito) |
<reinterpret_cast type>(Value) | Typecast (sempre eseguito) |
<const_cast type>(Value) | Typecast (sempre eseguito) |
Operazioni di valore
Operatore | significato |
---|---|
(tipo) Valore | Typecast (sempre eseguito) |
sizeof value | Dimensioni dell'espressione |
sizeof( tipo ) | Dimensioni del tipo di dati |
++ LValue | Incremento (prima della valutazione) |
-- LValue | Decremento (prima della valutazione) |
~ valore | Complemento bit |
! valore | Not (booleano) |
valore | Meno unario |
+ valore | Più unario |
& LValue | Indirizzo del tipo di dati |
valore | Dereference |
Struttura. Puntatore | Puntatore al membro della struttura |
Puntatore -> * Puntatore | Puntatore al membro della struttura a cui si fa riferimento |
Aritmetico
Operatore | significato |
---|---|
Valore valore | Moltiplicazione |
Valore valore / | Divisione |
Valore valore % | Modulo |
Valore valore + | Aggiunta |
Valore valore - | Sottrazione |
Valore valore<< | Spostamento bit per bit a sinistra |
Valore valore>> | Spostamento bit per bit a destra |
Valore valore< | Minore di (confronto) |
Value= Value< | Minore o uguale (confronto) |
Valore valore> | Maggiore di (confronto) |
Value= Value> | Maggiore o uguale (confronto) |
Valore valore == | Uguale (confronto) |
Valore != Valore | Diverso da (confronto) |
Valore e valore | AND bit per bit |
Valore valore ^ | XOR bit per bit (OR esclusivo) |
Valore valore | | OR bit per bit |
Valore & valore | AND logico |
Valore valore || | OR logico |
Gli esempi seguenti presuppongono che i pseudoregistri siano impostati come illustrato.
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
Assegnazione
Operatore | significato |
---|---|
Valore LValue = | Assegnazione |
Valore LValue *= | Moltiplicare e assegnare |
Valore LValue /= | Divisione e assegnazione |
Valore LValue %= | Modulo e assegnazione |
Valore LValue += | Aggiunta e assegnazione |
Valore LValue -= | Sottrazione e assegnazione |
LValue<<= Value | Sposta a sinistra e assegna |
LValue>>= Value | Spostare a destra e assegnare |
LValue &= Valore | AND e assegna |
Valore LValue |= | OR e assegnare |
Valore LValue ^= | XOR e assegna |
Valutazione
Operatore | significato |
---|---|
Valore ? Valore : Valore | Valutazione condizionale |
Valore , Valore | Valutare tutti i valori e quindi eliminare tutti tranne il valore più a destra |
Macro nelle espressioni C++
È possibile usare macro all'interno di espressioni C++. È necessario aggiungere un segno di numero (#) prima delle macro.
È possibile utilizzare le macro seguenti. Queste macro hanno le stesse definizioni delle macro di Microsoft Windows con lo stesso nome. Le macro di Windows sono definite in Winnt.h
.
Macro | Valore restituito |
---|---|
#CONTAINING_RECORD(Address, Type, Field) | Restituisce l'indirizzo di base di un'istanza di una struttura, in base al tipo della struttura e all'indirizzo di un campo all'interno della struttura. |
#FIELD_OFFSET(Type, Field) | Restituisce l'offset di byte di un campo denominato in un tipo di struttura noto. |
#RTL_CONTAINS_FIELD(Struct, Size, Field) | Indica se la dimensione dei byte specificata include il campo desiderato. |
#RTL_FIELD_SIZE(Type, Field) | Restituisce le dimensioni di un campo in una struttura di tipo noto, senza richiedere il tipo del campo. |
#RTL_NUMBER_OF(Array) | Restituisce il numero di elementi in una matrice con dimensioni statiche. |
#RTL_SIZEOF_THROUGH_FIELD(Type, Field) | Restituisce le dimensioni di una struttura di tipo noto, fino a e includendo un campo specificato. |
In questo esempio viene illustrato l'utilizzo della #FIELD_OFFSET
macro per calcolare l'offset dei byte in un campo di una struttura.
0:000> ?? #FIELD_OFFSET(_PEB, BeingDebugged)
long 0n2
Vedi anche
Espressioni MASM e espressioni C++