Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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++