Espressioni in C++ nativo
Il debugger accetta la maggior parte delle espressioni C/C++ Microsoft e ANSI.Il debugger consente inoltre a funzioni intrinseche e operatori di contesto per semplificare le espressioni di valutazione più sicure e più utili.In questo argomento vengono descritte le limitazioni relative alle espressioni C++ che devono essere a conoscenza quanto segue:
L'operatore di contesto e la maggior parte degli identificatori di formato non possono essere utilizzati nel codice né con espressioni di script o di codice gestitoin quanto sono specifici dell'analizzatore di espressioni C++ nativo.
Contenuto della sezione
Utilizzo di funzioni intrinisic del debugger per mantenere lo stato
Utilizzo di operatori di contesto per specificare un simbolo
Limitazioni relative alle espressioni in linguaggio C++ nativo
Controllo di accesso
Riferimenti ambigui
Spazi dei nomi anonimi
Costruttori, conversioni e distruttori
Ereditarietà
Funzioni intrinseche del compilatore e inline
Costanti numeriche
Funzioni degli operatori
Overload
Precedenza
Formati di simbolo
Cast di tipo
Utilizzo di funzioni intrinisic del debugger per mantenere lo stato
Le funzioni intrinseche del debugger offrono un modo per chiamare alcune funzioni C/C++ nelle espressioni senza modificare lo stato dell'applicazione.
Funzioni intrinseche del debugger:
Sono garantiti per accertarsi: esecuzione di una funzione intrinseca del debugger non danneggerà il processo che stia eseguendo il debug.
Sono consentiti nelle espressioni, anche negli scenari in cui gli effetti collaterali e la valutazione della funzione non sono consentiti.
Lavoro negli scenari in cui le chiamate di funzione e non sono possibili, come eseguire il debug di minidump.
Le funzioni intrinseche del debugger è inoltre possibile semplificare le espressioni di valutazione più utili.Ad esempio, strncmp(str, “asd”) è molto più facile da scrivere in una condizione del punto di interruzione che str[0] == ‘a’ && str[1] == ‘s’ && str[2] == ‘d’.)
Area |
Funzioni intrinseche |
---|---|
Lunghezza della stringa |
strlen, wcslen, strnlen, wcsnlen |
Confronto tra stringhe |
strcmp, wcscmp, stricmp, _stricmp, _strcmpi, wcsicmp, _wcscmpi, _wcsnicmp, strncmp, wcsncmp, strnicmp, wcsnicmp |
Ricerca di stringhe |
strchr, wcschr, strstr, wcsstr |
Win32 |
GetLastError(), TlsGetValue() |
Windows 8 |
WindowsGetStringLen(), WindowsGetStringRawBuffer() Queste funzioni richiedono il processo sottoposto a debug in esecuzione su Windows 8.I file dump di debug ha generato da Windows 8 che il dispositivo richiede inoltre che il computer che esegue Visual Studio è in esecuzione Windows 8.Tuttavia, se si esegue il debug di un dispositivo Windows 8 in modalità remota, il computer che esegue Visual Studio può essere Windows 7 in esecuzione. |
Varie |
__log2 Restituisce la base 2 del registro di un intero specificato, arrotondata all'intero più basso più vicino. |
Utilizzo di operatori di contesto per specificare un simbolo
L'operatore di contesto è un operatore aggiuntivo fornito dal debugger nativo.Quando il debug di codice nativo, è possibile utilizzare l'operatore di contesto per specificare la posizione di un punto di interruzione, un nome di variabile, o un'espressione.L'operatore di contesto è utile per specificare ad esempio un nome di un ambito esterno che verrebbe altrimenti nascosto da un nome locale.
Sintassi
{,,[modulo] } espressione
modulo è il nome di un modulo.È possibile utilizzare un percorso completo per distinguere tra i moduli con lo stesso nome.
espressione è qualsiasi espressione valida C++ che si risolve in un database di destinazione valido, ad esempio un nome di funzione, un nome di variabile, o un indirizzo del puntatore in modulo.
Le parentesi graffe devono contenere due virgole e il nome o il percorso completo del modulo (eseguibile o DLL).
Ad esempio, impostare un punto di interruzione nella funzione di SomeFunction di EXAMPLE.dll:
{,,EXAMPLE.dll}SomeFunction
Se il percorso di modulo include una virgola, uno spazio incorporato, o una parentesi graffa, è necessario utilizzare le virgolette intorno al percorso in modo da poter riconoscere correttamente il parser contesto della stringa.Poiché le virgolette singole vengono considerate parte di un nome di file di Windows, è necessario utilizzare le virgolette doppie.Di seguito è riportato un esempio:
{,"a long, long, library name.dll", } g_Var
Quando l'analizzatore di espressioni rileva un simbolo in un'espressione, ne esegue la ricerca nel seguente ordine:
Ambito lessicale verso l'esterno, a partire dal blocco corrente, una serie di istruzioni racchiuse tra parentesi graffe, verso il blocco di inclusione.Il blocco corrente è il codice contenente la posizione corrente, ovvero l'indirizzo del puntatore all'istruzione (instruction pointer).
Ambito della funzionecorrente.
Ambito della classe, se la posizione corrente è all'interno di una funzione membro C++.Nell'ambito della classe sono incluse tutte le classi base.L'analizzatore di espressioni utilizza le normali regole di dominanza.
Simboli globali del modulo corrente.
Simboli pubblici nel programma corrente.
Con l'operatore di contesto, il modulo iniziale della ricerca e si ignora la posizione corrente.
Limitazioni relative alle espressioni in linguaggio C++ nativo
Quando si immette un'espressione C/C++ in una finestra del debugger, vengono applicate le seguenti restrizioni generali:
Controllo di accesso
Il debugger può accedere a tutti i membri di classe indipendentemente dal controllo di accesso.È possibile esaminare qualsiasi membro di un oggetto classe, inclusi gli oggetti membro incorporati e le classi base.
Riferimenti ambigui
Se un'espressione del debugger fa riferimento a un nome di membro ambiguo, è necessario utilizzare il nome della classe per qualificarlo.Se ad esempio CObject è un'istanza di CClass, che eredita le funzioni membro denominate expense da AClass e BClass, CObject.expense risulterà ambiguo.È possibile risolvere tale ambiguità come segue:
CObject.BClass::expense
Per risolvere le ambiguità, l'analizzatore di espressioni applica le normali regole di dominanza relative ai nomi di membro.
Spazi dei nomi anonimi
L'analizzatore di espressioni in linguaggio C++ nativo non supporta gli spazi dei nomi anonimi.Si supponga, ad esempio, che esista il seguente codice:
#include "stdafx.h"
namespace mars
{
namespace
{
int test = 0;
}
}
int main()
{
// Adding a watch on test does not work.
mars::test++;
return 0;
}
L'unico modo per controllare il simbolo test in questo esempio consiste nell'utilizzare il nome decorato:
(int*)?test@?A0xccd06570@mars@@3HA
Costruttori, conversioni e distruttori
Non è possibile chiamare in modo implicito o esplicito un costruttore o un distruttore relativo a un oggetto, utilizzando un'espressione che richiede la costruzione di un oggetto temporaneo.La seguente espressione, ad esempio, chiama in modo esplicito un costruttore generando in questo modo un messaggio di errore:
Date( 2, 3, 1985 )
Se la destinazione della conversione è una classe, non sarà possibile chiamare una funzione di conversione.Tale conversione comporta la costruzione di un oggetto.Se, ad esempio, myFraction è un'istanza di CFraction che definisce l'operatore della funzione di conversione FixedPoint, la seguente espressione genererà un errore:
(FixedPoint)myFraction
Se la destinazione della conversione è un tipo incorporato, sarà tuttavia possibile chiamare una funzione di conversione.Se CFraction definisce una funzione di conversione operator float, la seguente espressione verrà considerata valida nel debugger:
(float)myFraction
È possibile chiamare le funzioni che restituiscono un oggetto o dichiarano oggetti locali.
Non è invece possibile chiamare gli operatori new e delete.La seguente espressione non funziona nel debugger:
new Date(2,3,1985)
Ereditarietà
Quando si utilizza il debugger per visualizzare un oggetto classe che include classi base virtuali, i membri della classe base virtuale vengono visualizzati per ciascun percorso di ereditarietà, anche se ne viene memorizzata una sola istanza.
Le chiamate di funzioni virtuali vengono gestite in modo corretto nell'analizzatore di espressioni.Si supponga, ad esempio, che la classe CEmployee definisca la funzione virtuale computePay ridefinita in una classe che eredita da CEmployee.È possibile chiamare computePay tramite un puntatore a CEmployee e fare in modo che venga eseguita la funzione appropriata:
empPtr->computePay()
È possibile eseguire il cast di un puntatore a un oggetto classe derivata in un puntatore a un oggetto classe base.È possibile eseguire il cast di un puntatore a un oggetto classe base in un puntatore a un oggetto classe derivata, ad eccezione di quando l'ereditarietà è virtuale.
Funzioni intrinseche del compilatore e inline
Un'espressione del debugger non può chiamare un compilatore funzioni intrinseche o funzione inline a meno che venga visualizzata almeno una volta come funzione normale.
Costanti numeriche
Le espressioni del debugger possono utilizzare le costanti integer nel formato ottale, esadecimale o decimale.Per impostazione predefinita, il debugger prevede costanti decimali.Questa impostazione può essere modificata nella pagina Generale della scheda Debug.
Per rappresentare i numeri in un'altra base, è possibile utilizzare i simboli di prefisso o di suffisso.Nella tabella riportata di seguito sono elencati i formati utilizzabili.
Sintassi |
Esempio (decimale 100) |
Base |
---|---|---|
cifre |
100 o 64 |
Decimale o esadecimale in base all'impostazione corrente |
0cifre |
0144 |
Ottale (base 8) |
0ncifre |
0n100 |
Decimale (base 10) |
0xcifre |
0x64 |
Esadecimale (base 16) |
cifreh |
64h |
Esadecimale (base 16) |
Funzioni degli operatori
Un'espressione del debugger può richiamare in modo implicito o esplicito le funzioni degli operatori relative a una classe.Si supponga, ad esempio, che myFraction e yourFraction siano istanze di una classe che definisce operator+.È possibile visualizzare la somma di questi due oggetti utilizzando la seguente espressione:
myFraction + yourFraction
Se la funzione di un operatore è definita come friend, è possibile chiamarla in modo implicito mediante la stessa sintassi utilizzata per una funzione membro oppure richiamarla in modo esplicito nel seguente modo:
operator+( myFraction, yourFraction )
Analogamente alle funzioni ordinarie, non è possibile chiamare le funzioni degli operatori con argomenti che richiedono una conversione che comporta la costruzione di un oggetto.
Nel debugger non sono supportati operatori di overload con entrambe le versioni const e non const.Tali operatori vengono spesso utilizzati nella Libreria di modelli standard.
Overload
Un'espressione del debugger può chiamare le funzioni in overload se esiste una corrispondenza esatta o se per una corrispondenza non è richiesta una conversione che comporti la costruzione di un oggetto.Se, ad esempio, la funzione calc accetta un oggetto CFraction come parametro e la classe CFraction definisce un costruttore con un singolo argomento che accetta un Integer, la seguente espressione genererà un errore:
calc( 23 )
Anche se è disponibile una conversione valida per convertire gli Integer nell'oggetto CFraction previsto da calc, questo tipo di conversione comporta la creazione di un oggetto e pertanto non è supportato.
Precedenza
Nelle espressioni del debugger l'operatore di ambito C++ (::) ha una precedenza più bassa rispetto a quando si trova nel codice sorgente.Nel codice sorgente C++ tale operatore ha la precedenza più alta.Nel debugger la precedenza rientra tra gli operatori base e suffissi (->, ++, --) e gli operatori unari (!, &, * e altri).
Formati di simbolo
È possibile immettere un'espressione del debugger contenente dei simboli nello stesso formato utilizzato nel codice sorgente, a condizione che tali simboli si trovino in un modulo compilato con informazioni di debug complete (/Zi o /ZI).Se viene immessa un'espressione contenente simboli pubblici, ovvero simboli disponibili nelle librerie o nei moduli compilati con /Zd, è necessario utilizzare il nome decorato del simbolo, ossia il formato utilizzato nel codice oggetto.Per ulteriori informazioni, vedere /Z7, /Zd, /Zi, /ZI (Formato informazioni di debug).
Specificando l'opzione di LINK /MAP, è possibile ottenere un elenco di tutti i nomi nei formati decorati e non decorati.Per ulteriori informazioni, vedere /MAP (Genera file Map).
La decorazione dei nomi è un meccanismo utilizzato per attivare il collegamento indipendente dai tipi.Questo significa che vengono collegati solo i nomi e i riferimenti con ortografia, maiuscole/minuscole, convenzione di chiamata e tipo perfettamente corrispondenti.
I nomi dichiarati con la convenzione di chiamata C, in modo implicito o esplicito mediante la parola chiave _cdecl, iniziano con un carattere di sottolineatura (_).La funzione main può ad esempio essere visualizzata come _main.I nomi dichiarati come _fastcall iniziano con il simbolo @.
Per il linguaggio C++, il nome decorato codifica il tipo del simbolo oltre alla convenzione di chiamata.Questo formato di nome può risultare lungo e di difficile lettura.Il nome inizia con almeno un punto interrogativo (?).Per le funzioni C++, la decorazione include l'ambito della funzione, i tipi dei parametri della funzione e il tipo restituito della funzione.
Cast di tipo
Se si esegue il cast a un tipo, è necessario che questo venga riconosciuto dal debugger.Nel programma deve essere presente un altro oggetto dello stesso tipo.I tipi creati mediante istruzioni typedef non sono supportati.