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 Lsuffissi , Ue 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 newoperatori , deletee 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++

?? valutare l'espressione C++

? evaluate expression

.expr choose expression evaluator

Firmare l'estensione

Esempi di espressioni miste