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 PUCHAR
cast 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 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 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 new
operatori , delete
e 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 && *myPtr
ad 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++
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per