Share via


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++ e espressioni MASM (Microsoft Macro Assembler). Ognuna di queste espressioni segue le proprie regole di sintassi per l'input e l'output.

Per altre informazioni su quando viene usato ogni tipo di sintassi, vedere Valutazione delle espressioni e del comando di valutazione dell'espressione ? .

Il parser di espressioni C++ supporta tutte le forme di sintassi delle espressioni C++. La sintassi include tutti i tipi di dati, inclusi i puntatori, i numeri a virgola mobile e le matrici e tutti gli operatori binari C++.

Le finestre Watch e Local nel debugger usano sempre l'analizzatore di espressioni C++.

Nell'esempio seguente, il comando di espressione ?? valuta C++ visualizza il valore del registro del puntatore 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 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 predefinite, è possibile usare il comando di valutazione dell'espressione ? 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 altre informazioni sul riferimento al registro, vedere Registrare la @eip sintassi.

In questo esempio viene aggiunto il valore esadecimale di 0xD al registro eip.

0:000> ? @eip + 0xD
Evaluate expression: 1998461455 = 771e1a0f

Registra e pseudoregistri nelle espressioni C++

È possibile usare registri e pseudoregistri all'interno di espressioni C++. Il segno @ deve essere aggiunto prima del registro o dello pseudoregistro.

L'analizzatore di espressioni esegue automaticamente il cast appropriato. I registri effettivi e i registri pseudovalore interi vengono cast in ULONG64. Tutti gli indirizzi vengono PUCHARcast in , $thread viene ETHREAD*eseguito il cast su , viene eseguito il cast su EPROCESS*, $teb$proc viene eseguito il cast su e $peb viene eseguito il cast in TEB*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 una pseudoregistrazione da un operatore di assegnazione o effetto collaterale. È necessario usare il comando r registers per modificare questi valori.

Nell'esempio seguente viene impostato il registro pseudo su un valore pari a 5 e quindi lo visualizza.

0:000> r $t0 = 5

0:000> ?? @$t0
unsigned int64 5

Per altre informazioni sui registri e sui pseudoregistrazioni, vedere Registrare la sintassi e la sintassi di pseudoregistro.

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 intero esadecimale, aggiungere 0x prima del numero. Per specificare un intero ottale, aggiungere 0 (zero) prima del numero.

Il debugger predefinito radix 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 il grave accento ('). 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 un riferimento al 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 il cast valido in un intero o in un puntatore, si verifica un errore di sintassi.

È possibile usare il prefisso (decimale) per alcuni output, ma non è possibile usarlo per l'input 0n dell'espressione C++.

Caratteri e stringhe nelle espressioni C++

È possibile immettere un carattere circostante con virgolette singole ('). I caratteri di escape C++ standard sono consentiti.

È possibile immettere valori letterali stringa circondandoli con virgolette doppie ("). È possibile usare \" come sequenza di escape all'interno di tale stringa. 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 relativo tipo. A seconda del simbolo a cui fa riferimento, può essere interpretato come intero, una struttura di dati, un puntatore a funzioni 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 usare un accento grave (') o un apostrofo (') in un nome di simbolo solo se si aggiunge un nome del modulo e un punto esclamativo prima del nome del simbolo. Quando si aggiungono i < delimitatori e > dopo un nome modello, è possibile aggiungere spazi tra questi delimitatori.

Se il simbolo potrebbe essere ambiguo, è possibile aggiungere un nome del 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 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 pseudoregistrazioni 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 si fa riferimento.

0:000> ??  @$teb->ClientId.UniqueProcess
void * 0x0000059c

Operatori nelle espressioni C++

È possibile usare parentesi per eseguire l'override delle regole di precedenza.

Se si racchiude 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 dell'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 e throw non è effettivamente possibile chiamare una funzione.

L'aritmetica puntatore è supportata e gli offset vengono ridimensionati correttamente. Si noti che non è possibile aggiungere un offset a un puntatore a una funzione. Se è necessario aggiungere un offset a un puntatore a una 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 tutte le regole principali vengono applicate. 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 sono della stessa precedenza e vengono analizzati da sinistra a destra.

Come per C++, la valutazione delle espressioni termina quando il valore è noto. Questa fine consente di usare in modo efficace espressioni, ?? myPtr && *myPtrad esempio .

Cast di riferimenti e tipi

Operatore Significato
Espressione // Commento Ignorare tutto il testo successivo
Classe :: Membro Membro della classe
Classe ::~Member Membro della classe (distruttore)
:: Nome Globale
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<digitare>(Valore) Typecast (sempre eseguito)
Static_cast<digitare>(Valore) Typecast (sempre eseguito)
Reinterpret_cast<digitare>(Valore) Typecast (sempre eseguito)
< const_castdigitare>(Valore) Typecast (sempre eseguito)

Operazioni sul valore

Operatore Significato
(tipo) Valore Typecast (sempre eseguito)
sizeofvalue Dimensioni dell'espressione
sizeof( type ) Dimensioni del tipo di dati
++ LValue Incremento (prima della valutazione)
-- LValue Decremento (prima della valutazione)
~ Valore Complemento di bit
! Valore Not (booleano)
Valore Operatore '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 Modulus
Valore + Valore Addizione
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 (confronto)
Valore != Valore Diverso da (confronto)
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 pseudo registri 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 Assegna
LValue *= Valore Moltiplicare e assegnare
LValue /= Valore Divisione e assegnazione
LValue %= Valore Modulo e assegnazione
LValue += Valore Aggiunta e assegnazione
LValue -= Valore Sottrazione e assegnazione
LValue<<= Valore Sposta a sinistra e assegna
LValue>>= Valore Sposta a destra e assegna
LValue &= Value AND e assegnare
LValue |= Valore OR e assegnare
LValue ^= Valore XOR e assegna

Versione di 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 di 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 di 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++

? espressione evaluate

.expr choose expression evaluator

Estensione firma

Esempi di espressioni miste