Condividi tramite


Espressioni in C++ nativo

Il debugger accetta la maggior parte delle espressioni C/C++ Microsoft e ANSI. Il debugger fornisce inoltre funzioni intrinseche e operatori di contesto per rendere più sicura e pratica la valutazione delle espressioni. In questo argomento vengono descritte le restrizioni sulle espressioni C++ di cui occorre essere consapevoli:

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 gestito in quanto sono specifici dell'analizzatore di espressioni C++ nativo.

Contenuto della sezione

Utilizzo di funzioni intrinseche del debugger per mantenere lo stato

Utilizzo di operatori di contesto per specificare un simbolo

Restrizioni sulle espressioni C++ native

  • Controllo di accesso

  • Riferimenti ambigui

  • Spazi dei nomi anonimi

  • Costruttori, distruttori e conversioni

  • Ereditarietà

  • Funzioni intrinseche inline e del compilatore

  • Costanti numeriche

  • Funzioni degli operatori

  • Overload

  • Precedenza

  • Formati di simbolo

  • Cast di tipo

Utilizzo di funzioni intrinseche del debugger per mantenere lo stato

Le funzioni intrinseche del debugger consentono di chiamare alcune funzioni C/C++ nelle espressioni senza modificare lo stato dell'applicazione.

Funzioni intrinseche del debugger:

  • Garantite come sicure: l'esecuzione di una funzione intrinseca del debugger non danneggia il processo sottoposto a debug.

  • Consentite in tutte le espressioni, anche negli scenari in cui gli effetti collaterali e la valutazione delle funzioni non sono consentiti.

  • Supportate negli scenari in cui le chiamate a funzioni regolari non sono possibili, ad esempio il debug di un minidump.

Le funzioni intrinseche del debugger rendono più pratica la valutazione delle espressioni. Ad esempio, è molto più facile scrivere strncmp(str, “asd”) in una condizione del punto di interruzione di str[0] == ‘a’ && str[1] == ‘s’ && str[2] == ‘d’. )

Area

Funzioni intrinseche

Lunghezza delle stringhe

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 che il processo sottoposto a debug sia eseguito in Windows 8. Il debug dei file dump generati da un dispositivo Windows 8 richiede inoltre che nel computer Visual Studio sia eseguito Windows 8. Tuttavia, se si esegue il debug di un dispositivo Windows 8 in modalità remota, nel computer Visual Studio può essere eseguito Windows 7.

Varie

__log2

Restituisce il logaritmo in base 2 dell'intero specificato, arrotondato all'intero più basso più prossimo.

Utilizzo di operatori di contesto per specificare un simbolo

L'operatore di contesto è un operatore aggiuntivo fornito dal debugger nativo. Durante il debug di codice nativo, è possibile utilizzare l'operatore di contesto per qualificare la posizione di un punto di interruzione, il nome di una variabile o un'espressione. L'operatore di contesto è utile per specificare un nome da un ambito esterno che altrimenti sarebbe nascosto da un nome locale.

Sintassi

{,,[module] } expression

  • module è il nome di un modulo. È possibile utilizzare un percorso completo per distinguere i moduli che hanno lo stesso nome.

  • expression è qualsiasi espressione C++ valida che viene risolta in una destinazione valida, ad esempio un nome di funzione, un nome di variabile o un indirizzo di puntatore in module.

Le parentesi graffe devono contenere due virgole e il nome o il percorso completo del modulo (file eseguibile o DLL).

Ad esempio, per impostare un punto di interruzione nella funzione SomeFunction di EXAMPLE.dll:

{,,EXAMPLE.dll}SomeFunction

Se il percorso di module include una virgola, uno spazio incorporato o una parentesi graffa, dovrà essere racchiuso tra virgolette in modo da consentire al parser del contesto di riconoscere la stringa correttamente. 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:

  1. Ambito lessicale verso l'esterno, a partire dal blocco corrente, 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.

  2. Ambito della funzione. Funzione corrente.

  3. Ambito della classe, se la posizione corrente si trova all'interno di una funzione membro C++. Nell'ambito della classe sono incluse tutte le classi base. L'analizzatore di espressioni utilizza le regole di dominanza normali.

  4. Simboli globali nel modulo corrente.

  5. Simboli pubblici nel programma corrente.

L'operatore di contesto consente di specificare il modulo iniziale della ricerca e ignorare la posizione corrente.

Restrizioni sulle espressioni C++ native

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 della 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à nel modo seguente:

CObject.BClass::expense

Per risolvere le ambiguità, l'analizzatore di espressioni applica le normali regole di dominanza relative ai nomi dei membri.

Spazi dei nomi anonimi

L'analizzatore di espressioni C++ native non supporta gli spazi dei nomi anonimi. Si supponga, ad esempio, che sia presente 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, distruttori e conversioni

Non è possibile chiamare in modo implicito o esplicito un costruttore o un distruttore per un oggetto, utilizzando un'espressione che richiede la costruzione di un oggetto temporaneo. La seguente espressione, ad esempio, chiama in modo esplicito un costruttore e genera un messaggio di errore:

Date( 2, 3, 1985 )

Se la destinazione della conversione è una classe, non è 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 genera un errore:

(FixedPoint)myFraction

Se la destinazione della conversione è un tipo incorporato, è 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 funzioni che restituiscono un oggetto o dichiarano oggetti locali.

Non è 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 viene memorizzata una sola istanza di questi membri.

Le chiamate di funzioni virtuali vengono gestite in modo corretto dall'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 attraverso un puntatore a CEmployee ed eseguire 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, tranne nel caso in cui l'ereditarietà sia virtuale.

Funzioni intrinseche inline e del compilatore

Le espressioni del debugger non possono chiamare funzioni intrinseche del compilatore o funzioni inline, a meno che tale funzione non compaia almeno una volta come funzione normale.

Costanti numeriche

Le espressioni del debugger possono utilizzare costanti Integer nel formato ottale, esadecimale o decimale. Per impostazione predefinita, il debugger si aspetta costanti decimali. Questa impostazione può essere modificata nella pagina Generale della scheda Debug.

Per rappresentare numeri in un'altra base, è possibile utilizzare i simboli di prefisso o suffisso. Nella tabella riportata di seguito sono indicati i formati utilizzabili.

Sintassi

Esempio (decimale 100)

Base

digits

100 o 64

Decimale o esadecimale in base all'impostazione corrente.

0 digits

0144

Ottale (base 8)

0n digits

0n100

Decimale (base 10)

0x digits

0x64

Esadecimale (base 16)

digits h

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 viene definita come friend, è possibile chiamarla in modo implicito utilizzando 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.

Il debugger non supporta operatori di overload con entrambe le versioni const e non const. Questi 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 intero, la seguente espressione genera un errore:

calc( 23 )

Anche se esiste una conversione valida per convertire l'intero 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++ questo operatore ha la precedenza più alta. Nel debugger la precedenza rientra tra gli operatori base e suffisso (->, ++, --) e gli operatori unari (!, &, * e altri).

Formati di simbolo

È possibile immettere un'espressione del debugger contenente 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 si immette un'espressione che contiene simboli pubblici, ovvero simboli disponibili nelle librerie o nei moduli compilati con /Zd, è necessario utilizzare il nome decorato del simbolo, ovvero il formato utilizzato nel codice oggetto. Per ulteriori informazioni, vedere /Z7, /Zd, /Zi, /ZI (Formato informazioni di debug).

Specificando l'opzione LINK /MAP, è possibile ottenere un elenco di tutti i nomi nei formati decorati e non. Per ulteriori informazioni, vedere /MAP (Genera file Map).

La decorazione dei nomi è un meccanismo utilizzato per attivare il collegamento indipendente dai tipi. Ciò 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ò 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.