Partilhar via


Expressões em C++ nativo

O depurador aceita a maioria das expressões C/C++ e Microsoft. O depurador também fornece funções intrínsecas e operadores de contexto para criar expressões de avaliação mais seguras e mais convenientes. Este tópico também descreve as limitações em expressões C++. Para usá-las, você precisa estar ciente do seguinte:

Você não pode usar o operador de contexto ou a maioria dos especificadores de formato em código ou com expressões de script ou código gerenciado. Elas são específicas para o avaliador de expressão C++ nativa.

Nesta seção

Usando funções intrínsecas do depurador para manter o estado

Usando operadores de contexto para especificar um símbolo

Restrições em expressões C++ nativas

  • Controle de acesso

  • Referências ambíguas

  • Namespaces anônimos

  • Construtores, destruidores e conversões

  • Herança

  • Funções embutidas e intrínsecas do compilador

  • Constantes numéricas

  • Funções do operador

  • Sobrecarga

  • Precedência

  • Formatos de símbolo

  • Conversão de tipo

Usando funções intrínsecas do depurador para manter o estado

As funções intrínsecas do depurador oferecem uma maneira de chamar determinadas funções C/C++ em expressões sem alterar o estado do aplicativo.

Funções intrínsecas do depurador:

  • São certamente seguras: executar uma função intrínseca do depurador não corromperá o processo que está sendo depurado.

  • São permitidas em todas as expressões, mesmo em cenários onde os efeitos colaterais e a avaliação de função não são permitidos.

  • Trabalham em cenários onde as chamadas de funções normais não são possíveis, por exemplo, depurar um minidespejo.

As funções intrínsecas do depurador também podem tornar mais convenientes as expressões de avaliação. Por exemplo, strncmp(str, “asd”) é muito mais fácil de escrever em uma condição de ponto de interrupção que str[0] == ‘a’ && str[1] == ‘s’ && str[2] == ‘d’. )

Área

Funções intrínsecas

Comprimento de cadeias de caracteres

strlen, wcslen, strnlen, wcsnlen

Comparação de cadeias de caracteres

strcmp, wcscmp, stricmp, _stricmp, _strcmpi, wcsicmp, _wcscmpi, _wcsnicmp, strncmp, wcsncmp, strnicmp, wcsnicmp

Pesquisa de cadeias de caracteres

strchr, wcschr, strstr, wcsstr

Win32

GetLastError(), TlsGetValue()

Windows 8

WindowsGetStringLen(), WindowsGetStringRawBuffer()

Essas funções exigem que o processo que está sendo depurado seja executado no Windows 8. Depurar os arquivos de despejo gerados a partir de um dispositivo do Windows 8 também exige que o computador do Visual Studio esteja executando o Windows 8. No entanto, se você estiver depurando um dispositivo do Windows 8 remotamente, o computador do Visual Studio poderá executar o Windows 7.

Diversos

__log2

Retorna a base 2 de log de um inteiro especificado, arredondada para o menor inteiro próximo.

Usando operadores de contexto para especificar um símbolo

O operador de contexto é um operador adicional fornecido pelo depurador nativo. Ao depurar o código nativo, você poderá usar o operador de contexto para qualificar um local de ponto de interrupção, nome de variável ou expressão. O operador de contexto é útil para fins como especificar um nome de um escopo externo que, de outra forma, seria ocultado por um nome local.

Sintaxe

{,,[module] } expression

  • module é o nome de um módulo. Você pode usar um caminho completo para resolver a ambiguidade entre módulos com o mesmo nome.

  • expression é qualquer expressão C++ válida que seja resolvida para um destino válido, como um nome de função, nome de variável ou endereço do ponteiro no module.

As chaves devem conter duas vírgulas e o nome do módulo (executável ou DLL) ou o caminho completo.

Por exemplo, para definir um ponto de interrupção na função SomeFunction de EXAMPLE.dll:

{,,EXAMPLE.dll}SomeFunction

Se o caminho do module inclui uma vírgula, um espaço inserido ou uma chave, você deverá usar aspas em torno do caminho de modo que o analisador de contexto possa reconhecer corretamente a cadeia de caracteres. As aspas simples são consideradas parte de um nome de arquivo do Windows, portanto, você deve usar aspas duplas. Por exemplo,

{,"a long, long, library name.dll", } g_Var

Quando o avaliador de expressão localiza um símbolo em uma expressão, procura pelo símbolo na seguinte ordem:

  1. Escopo léxico externo, começando com o bloco atual, série de instruções incluídas entre chaves e a continuação externa com o bloco delimitador. O bloco atual é o código que contém o local atual, endereço do ponteiro de instrução.

  2. Escopo da função. A função atual.

  3. Escopo da classe, se o local atual estiver dentro de uma função de membro C ++. O escopo da classe inclui todas as classes base. O avaliador de expressão usa regras de dominância normais.

  4. Símbolos globais no módulo atual.

  5. Símbolos públicos no programa atual.

Com o operador de contexto, você especifica o módulo inicial da pesquisa e ignora o local atual.

Restrições em expressões C++ nativas

Quando você insere a expressão C/C++ em uma janela do depurador, essas restrições gerais se aplicam:

Controle de acesso

O depurador pode acessar todos os membros de classe independentemente do controle de acesso. Você pode examinar qualquer membro do objeto de classe, incluindo classes base e objetos de membro inseridos.

Referências ambíguas

Se uma expressão do depurador se refere a um nome de membro ambíguo, você deverá usar o nome da classe para qualificá-lo. Por exemplo, se CObject for uma instância de CClass, que herda as funções de membro chamadas expense do AClass e BClass, CObject.expense será ambígua. Você pode resolver a ambiguidade da seguinte maneira:

CObject.BClass::expense

Para resolver ambiguidades, o avaliador de expressão aplica regras de dominância normais em relação aos nomes de membro.

Namespaces anônimos

O avaliador de expressão C++ nativa não oferece suporte a namespaces anônimos. Suponha, por exemplo, que você tenha o seguinte código:

#include "stdafx.h"

namespace mars 
{ 
    namespace
    {
        int test = 0; 
    } 

} 


int main() 
{ 
    // Adding a watch on test does not work. 
    mars::test++; 
    return 0; 
} 

A única maneira de observar o símbolo test neste exemplo é usar o nome decorado:

(int*)?test@?A0xccd06570@mars@@3HA

Construtores, destruidores e conversões

Você não pode chamar um construtor ou um destruidor para um objeto, explicitamente ou implicitamente, usando uma expressão que exige a construção de um objeto temporário. Por exemplo, a expressão a seguir chama explicitamente um construtor e resulta em uma mensagem de erro:

Date( 2, 3, 1985 )

Você não pode chamar uma função de conversão se o destino da conversão for uma classe. Essa conversão envolve a construção de um objeto. Por exemplo, se myFraction for uma instância de CFraction, que define o operador da função de conversão FixedPoint, a expressão a seguir resultará em erro:

(FixedPoint)myFraction

No entanto, você pode chamar uma função de conversão se o destino da conversão for um tipo interno. Se CFraction definir uma função de conversão operator float, a seguinte expressão será válida no depurador:

(float)myFraction

Você pode chamar funções que retornam um objeto ou declaram objetos locais.

Você não pode chamar os operadores new ou delete. A expressão a seguir não funciona no depurador:

new Date(2,3,1985)

Herança

Quando você usar o depurador para exibir um objeto de classe que tem as classes base virtuais, os membros da classe base virtual serão exibidos para cada caminho de herança, embora apenas uma instância desses membros seja armazenada.

As chamadas de função virtual são tratadas corretamente pelo avaliador de expressão. Por exemplo, suponha que a classe CEmployee defina uma função virtual computePay, que é redefinida em uma classe que herda de CEmployee. Você pode chamar computePay por um ponteiro para CEmployee e a função adequada ser executada:

empPtr->computePay()

Você pode converter um ponteiro para um objeto de classe derivada em um ponteiro para um objeto de classe base. Você pode converter um ponteiro para um objeto de classe base em um ponteiro para um objeto de classe derivada, exceto quando a herança for virtual.

Funções embutidas e intrínsecas do compilador

Uma expressão do depurador não pode chamar funções intrínsecas ou uma função embutida de um compilador a menos que a função apareça pelo menos uma vez como uma função normal.

Constantes numéricas

As expressões do depurador podem usar constantes de inteiro em formato octal, hexadecimal ou decimal. Por padrão, o depurador espera constantes decimais. Essa configuração pode ser alterada na página Geral da guia Depurando.

Você pode usar símbolos de prefixo ou sufixo para representar números em outra base. A tabela a seguir mostra os formatos que você pode usar.

Sintaxe

Exemplo (decimal 100)

Base

digits

100 ou 64

Decimal ou hexadecimal, dependendo da configuração atual.

0 digits

0144

Octal (base 8)

0n digits

0n100

Decimal (base 10)

0x digits

0x64

Hexadecimal (base 16)

digits h

64h

Hexadecimal (base 16)

Funções do operador

Uma expressão do depurador pode invocar funções do operador para uma classe implícita ou explicitamente. Por exemplo, suponha que myFraction e yourFraction sejam instâncias de uma classe que define operator+. Você pode exibir a soma desses dois objetos usando essa expressão:

myFraction + yourFraction

Se uma função do operador for definida como um amigo, você poderá chamá-lo implicitamente usando a mesma sintaxe que uma função de membro ou pode invocá-la explicitamente, da seguinte maneira:

operator+( myFraction, yourFraction )

Como funções comuns, as funções do operador não podem ser chamadas com argumentos que exigem uma conversão que envolve a construção do objeto.

O depurador não dá suporte a operadores sobrecarregados com versões const e não const. Os operadores sobrecarregados com versões const e não const são usados com frequência na biblioteca de modelos padrão.

Sobrecarga

Uma expressão do depurador pode chamar funções sobrecarregadas se existir uma correspondência exata ou se uma correspondência não exigir uma conversão que envolva a construção do objeto. Por exemplo, se a função calc utilizar um objeto CFraction como um parâmetro e a classe CFraction definir um construtor de argumento único que aceita um inteiro, a seguinte expressão resultará em um erro:

calc( 23 )

Mesmo que uma conversão legal exista para converter o inteiro no objeto CFraction que calc espera, essa conversão envolverá a criação de um objeto e não terá suporte.

Precedência

Em expressões de depurador, o operador do escopo C++ (::) tem uma precedência mais baixa do que no código-fonte. No código-fonte C++, esse operador tem a precedência mais alta. No depurador, sua prioridade está entre os operadores base e de sufixo (->, ++, --) e os operadores unários (!, &, *, dentre outros).

Formatos de símbolo

Você insere uma expressão de depurador que contém símbolos no mesmo formato usado no código-fonte, contanto que os símbolos estejam em um módulo compilado com informações de depuração completas (/Zi ou /ZI). Se você inserir uma expressão que contém símbolos públicos, que são símbolos encontrados em bibliotecas ou módulos compilados com /Zd, deverá usar o nome decorado do símbolo, o formato usado no código do objeto. Para obter mais informações, consulte /Z7, /Zd, /Zi, /ZI (formato de informação de depuração).

Você pode obter uma lista de todos os nomes em suas formas decoradas e não decoradas usando a opção LINK /MAP. Para obter mais informações, consulte /MAP (Gerar Mapfile).

A decoração de nome é o mecanismo usado para impor vinculação fortemente tipada. Isso significa que somente os nomes e as referências com ortografia, casos, convenção de chamada e tipos precisamente compatíveis são vinculados.

Os nomes declarados com a convenção de chamada de C, implícita ou explicitamente usando a palavra-chave _cdecl, começam com um sublinhado ( _ ). Por exemplo, a função main pode ser exibida como _main. Os nomes declarados como _fastcall começam com o símbolo @.

Para C++, o nome decorado codifica o tipo do símbolo além da convenção de chamada. O formato do nome pode ser longo e difícil de ler. O nome começa com pelo menos um ponto de interrogação (?). Para funções C++, a decoração inclui o escopo da função, os tipos de parâmetros da função e o tipo de retorno de função.

Conversão de tipo

Se você converter em um tipo, o tipo deverá ser conhecido do depurador. Você deve ter outro objeto desse tipo em seu programa. Os tipos criados usando instruções typedef não têm suporte.