Condividi tramite


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 Osservazione e 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 .expr choose expression 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 delle istruzioni.

0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02

Per ulteriori informazioni sulla sintassi del registro, vedere Sintassi del registro@eip.

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 convertiti in ULONG64. Viene eseguito il cast di tutti gli indirizzi a PUCHAR, $thread è castato su ETHREAD*, $proc è castato su EPROCESS*, $teb è castato su TEB*, e $peb è castato su PEB*.

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 un pseudo-registro tramite un operatore di assegnazione o di effetti collaterali. 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 ulteriori informazioni sui registri e sugli pseudoregistri, vedere sintassi dei registri e sintassi degli pseudoregistri.

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 suffissi L, U e I64 con valori interi. 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 convertire i valori interi in 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 dei pseudo-registri ai tipi corretti. Ad esempio, $teb viene eseguito il cast come un 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 del processo nella struttura TEB, mostrando l'uso di un puntatore a un membro della struttura di 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 di chiocciola 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 ([ ]), membri puntatore (->), membri UDT . e 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 devi aggiungere un offset a un puntatore a funzione, esegui prima il cast dell'offset su un puntatore a char.

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.

Conversione di riferimenti e tipi

Operatore Significato
Espressione // Commento Ignora tutto il testo successivo
Classe :: Membro Membro della classe
Classe ::~Membro Membro di classe (distruttore)
:: Nome Generale
Struttura . Campo Campo in una struttura
Puntatore ->Campo Campo nella struttura a cui si fa riferimento
Nome [integer] Indice di matrice
LValue ++ Incremento (dopo la valutazione)
LValue -- Decremento (dopo la valutazione)
dynamic_cast<tipo>(Valore) Typecast (sempre eseguito)
< static_casttype>(Valore) Typecast (sempre eseguito)
reinterpret_cast<tipo>(Valore) Typecast (sempre eseguito)
< const_casttipo>(Valore) Typecast (sempre eseguito)

Operazioni di valore

Operatore Significato
(tipo) Valore Typecast (sempre eseguito)
sizeofvalue Dimensioni dell'espressione
sizeof( tipo ) Dimensioni del tipo di dati
++ LValue Incremento (prima della valutazione)
-- LValue Decremento (prima della valutazione)
~ Valore Complemento bit
! Valore Non (booleano)
Valore Meno unario
+ Valore Più unario
& LValue Indirizzo del tipo di dati
Valore Dereferenziare
Struttura . puntatore Puntatore al membro della struttura
Puntatore -> * Puntatore Puntatore al membro della struttura a cui si fa riferimento

Aritmetica

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)
Valore<= Valore Minore o uguale (confronto)
Valore>Valore Maggiore di (confronto)
Valore>= Valore Maggiore o uguale (confronto)
Valore == Valore Uguale per comparazione
Valore != Valore Non uguale (confronto)
Valore e valore AND bit per bit
Valore ^ Valore XOR bit per bit (OR esclusivo)
Valore | Valore OR bit per bit
Valore & valore E logico
Valore || Valore oppure 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
LValue = Valore Assegnare
LValue *= Valore Moltiplicare e assegnare
LValue /= Valore Dividere e assegnare
LValue %= Valore Modulo e assegnazione
LValue += Valore Aggiungere e assegnare
LValue -= Valore Sottrarre e assegnare
LValue<<= Valore Sposta a sinistra e assegna
LValue>>= Valore Spostare a destra e assegnare
LValue &= Valore AND e assegna
LValue |= Valore OR e assegnare
LValue ^= Valore 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(Tipo, Campo) 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 la dimensione di una struttura di tipo noto, inclusi i campi fino a quello 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

Vedere anche

Espressioni MASM e espressioni C++

?? valutare l'espressione C++

? valutare espressione

.expr scegli valutatore di espressione

Estensione del segno

Esempi di espressioni miste