Compartilhar via


Números e operadores do C++

Este artigo descreve o uso da sintaxe de expressão C++ com as ferramentas de depuração do Windows.

O depurador aceita dois tipos diferentes de expressões numéricas: expressões C++ e expressões MASM (Microsoft Macro Assembler). Cada uma dessas expressões segue suas próprias regras de sintaxe para entrada e saída.

Para obter mais informações sobre quando cada tipo de sintaxe é usado, consulte Avaliando expressões e o comando ? avaliar expressão .

O analisador de expressões C++ dá suporte a todas as formas de sintaxe de expressão C++. A sintaxe inclui todos os tipos de dados, incluindo ponteiros, números de ponto flutuante e matrizes, e todos os operadores unários e binários do C++.

As Inspeção e as Locais no depurador sempre usam o avaliador de expressão C++.

No exemplo a seguir, o comando de expressão ?? evaluate C++ exibe o valor do registro do ponteiro de instrução.

0:000> ?? @eip
unsigned int 0x771e1a02

Podemos usar a função C++ sizeof para determinar o tamanho das estruturas.

0:000> ?? (sizeof(_TEB))
unsigned int 0x1000

Definir o avaliador de expressão como C++

Use o avaliador de expressão .expr choose para visualizar o avaliador de expressão padrão e alterá-lo para C++.

0:000> .expr
Current expression evaluator: MASM - Microsoft Assembler expressions
0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions

Depois que o avaliador de expressão padrão for alterado, o comando ? avaliar expressão poderá ser usado para exibir expressões C++. O exemplo a seguir exibe o valor do registro do ponteiro de instrução.

0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02

Para saber mais sobre a referência do @eip registro, consulte a sintaxe de registro.

Neste exemplo, o valor hex de 0xD é adicionado ao registro de eip.

0:000> ? @eip + 0xD
Evaluate expression: 1998461455 = 771e1a0f

Registros e pseudo-registros em expressões C++

Você pode usar registros e pseudo-registros em expressões C++. O sinal @ deve ser adicionado antes do registro ou pseudo-registro.

O avaliador de expressão executa automaticamente a conversão adequada. Registros reais e pseudo-registros de valor inteiro são convertidos em ULONG64. Todos os endereços são convertidos em PUCHAR, $thread é convertido em ETHREAD*, $proc é convertido em EPROCESS*, $teb é convertido em TEB*, e $peb é convertido em PEB*.

Este exemplo exibe o 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

Você não pode alterar um registro ou pseudo-registro usando um operador de atribuição ou com efeito colateral. Você deve usar o comando r registers para alterar esses valores.

O exemplo a seguir define o pseudo-registro como um valor de 5 e o exibe.

0:000> r $t0 = 5

0:000> ?? @$t0
unsigned int64 5

Para obter mais informações sobre registros e pseudo-registros, consulte Sintaxe Register e sintaxe pseudo-registro.

Números em expressões C++

Os números em expressões C++ são interpretados como números decimais, a menos que você os especifique de outra maneira. Para especificar um inteiro hexadecimal, adicione 0x antes do número. Para especificar um inteiro octal, adicione 0 (zero) antes do número.

O radix de depurador padrão não afeta a forma como você insere expressões C++. Você não pode inserir diretamente um número binário, exceto aninhando uma expressão MASM dentro da expressão C++.

Você pode inserir um valor hexadecimal de 64 bits no formato xxxxxxxx'xxxxxxxxx. Você também pode omitir o acento grave ('). Ambos os formatos produzem o mesmo valor.

Você pode usar os sufixos L, U e I64 com valores inteiros. O tamanho real do número criado depende do sufixo e do número que você insere. Para obter mais informações sobre essa interpretação, consulte uma referência de linguagem C++.

A saída do avaliador de expressão C++ mantém o tipo de dados que as regras de expressão C++ especificam. No entanto, se você usar essa expressão como um argumento para um comando, uma conversão sempre será realizada. Por exemplo, você não precisa converter valores inteiros em ponteiros quando eles são usados como endereços em argumentos de comando. Se o valor da expressão não puder ser convertido em um inteiro ou um ponteiro, ocorrerá um erro de sintaxe.

Você pode usar o 0n prefixo (decimal) para alguma saída, mas não pode usá-lo para entrada de expressão C++.

Caracteres e cadeias de caracteres em expressões C++

Você pode inserir um caractere cercando-o com aspas simples ('). Os caracteres de escape C++ padrão são permitidos.

Você pode inserir literais de cadeia de caracteres cercando-os com aspas duplas ("). Você pode usar \" como uma sequência de escape dentro dessa cadeia de caracteres. No entanto, as cadeias de caracteres não têm nenhum significado para o avaliador de expressão.

Símbolos em expressões C++

Em uma expressão C++, cada símbolo é interpretado de acordo com seu tipo. Dependendo do que o símbolo se refere, ele pode ser interpretado como um inteiro, uma estrutura de dados, um ponteiro de função ou qualquer outro tipo de dados. Um erro de sintaxe ocorrerá se você usar um símbolo que não corresponda a um tipo de dados C++, como um nome de módulo não modificado, dentro de uma expressão C++.

Você pode usar um acento grave (') ou um apóstrofo (') em um nome de símbolo somente se você adicionar um nome de módulo e ponto de exclamação antes do nome do símbolo. Ao adicionar os < delimitadores e > delimitadores após um nome de modelo, você pode adicionar espaços entre esses delimitadores.

Se o símbolo puder ser ambíguo, você poderá adicionar um nome de módulo e um ponto de exclamação (!) ou apenas um ponto de exclamação antes do símbolo. Para especificar que um símbolo deve ser local, omita o nome do módulo e inclua um sinal de dólar e um ponto de exclamação ($!) antes do nome do símbolo. Para obter mais informações sobre o reconhecimento de símbolos, consulte Sintaxe de símbolo e correspondência de símbolos.

Estruturas em expressões C++

O avaliador de expressão C++ converte pseudo-registros em seus tipos apropriados. Por exemplo, $teb é convertido como um 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

O exemplo a seguir exibe a ID do processo na estrutura TEB mostrando o uso de um ponteiro para um membro da estrutura referenciada.

0:000> ??  @$teb->ClientId.UniqueProcess
void * 0x0000059c

Operadores em expressões C++

Você pode usar parênteses para substituir regras de precedência.

Se você colocar parte de uma expressão C++ entre parênteses e adicionar duas arrobas (@@) antes da expressão, a expressão será interpretada de acordo com as regras de expressão MASM. Você não pode adicionar um espaço entre os dois em sinais e os parênteses de abertura. O valor final dessa expressão é passado para o avaliador de expressão C++ como um valor ULONG64. Você também pode especificar o avaliador de expressão usando @@c++( ... ) ou @@masm( ... ).

Os tipos de dados são indicados como de costume na linguagem C++. Os símbolos que indicam matrizes ([ ]), membros de ponteiro (->), membros UDT (.) e membros de classes (::) são todos reconhecidos. Todos os operadores aritméticos têm suporte, incluindo operadores de atribuição e efeito colateral. No entanto, você não pode usar os operadores new, delete e throw, e não é possível realmente chamar uma função.

Há suporte para a aritmética de ponteiro e os deslocamentos são dimensionados corretamente. Observe que você não pode adicionar um deslocamento a um ponteiro de função. Se você precisar adicionar um deslocamento a um ponteiro de função, converta o deslocamento em um ponteiro de caractere primeiro.

Como no C++, se você usar operadores com tipos de dados inválidos, ocorrerá um erro de sintaxe. O analisador de expressões C++ do depurador usa regras um pouco mais relaxadas do que a maioria dos compiladores C++, mas todas as regras essenciais são impostas. Por exemplo, você não pode deslocar um valor não inteiro.

Você pode usar os operadores a seguir. Os operadores em cada célula têm precedência sobre operadores em células inferiores. Os operadores na mesma célula têm a mesma precedência e são analisados da esquerda para a direita.

Assim como acontece com C++, a avaliação de expressão termina quando seu valor é conhecido. Essa finalização permite que você use efetivamente expressões como ?? myPtr && *myPtr.

Referência e conversão de tipos

Operador Significado
Expressão // Comentário Ignorar todo o texto subsequente
Classe :: Membro Membro da classe
Classe ::~Membro Membro da classe (destruidor)
:: Nome Mundial
Estrutura . Campo Campo em uma estrutura
Ponteiro ->Campo Campo na estrutura referenciada
Nome [inteiro] Índice de matriz
Lvalue ++ Incremento (após a avaliação)
Lvalue -- Decremento (após avaliação)
dynamic_cast<tipo>(Valor) Typecast (sempre executado)
static_cast<tipo>(Valor) Typecast (sempre executado)
reinterpret_cast<tipo>(Valor) Typecast (sempre executado)
const_cast<type>(Valor) Typecast (sempre executado)

Operações de valor

Operador Significado
(tipo) Valor Typecast (sempre executado)
sizeofvalue Tamanho da expressão
sizeof( type ) Tamanho do tipo de dados
++ Lvalue Incremento (antes da avaliação)
-- Lvalue Decremento (antes da avaliação)
~ de Complemento de bits
! Valor Não (booleano)
Valor Menos unário
+ de Adição de unário
& LValue Endereço do tipo de dados
Valor Desreferenciar
Estrutura . Ponteiro Ponteiro para membro da estrutura
Ponteiro -> * Ponteiro Ponteiro para membro da estrutura referenciada

Aritmético

Operador Significado
Valor do valor Multiplicação
Valor / Valor Divisão
Valor % Valor Módulo
Valor + Valor Adição
Valor - Valor Subtração
Valor<<Valor Deslocamento bit a bit para a esquerda
Valor>>Valor Deslocamento de bits para a direita
Valor<Valor Menor que (comparação)
Valor<= Valor Menor ou igual (comparação)
Valor>Valor Maior que (comparação)
Valor>= Valor Maior ou igual (comparação)
Valor == Valor Igual (comparação)
Valor != Valor Não é igual (comparação)
Valor & Valor AND bit a bit
Valor ^ Valor Operação bit a bit XOR (ou exclusivo)
Valor | Valor OR bit a bit
Valor && Valor AND lógico
Valor || Valor OR lógico

Os exemplos a seguir pressupõem que os pseudo-registros sejam definidos conforme mostrado.

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

Atribuição

Operador Significado
Lvalue = Valor Assign
Lvalue *= Valor Multiplicar e atribuir
Lvalue /= Valor Dividir e atribuir
Lvalue %= Valor Modulo e atribuir
Lvalue += Valor Adicionar e atribuir
Lvalue -= Valor Subtrair e atribuir
Lvalue<<= Valor Movimentar para a esquerda e atribuir
Lvalue>>= Valor Deslocar para a direita e atribuir
LValue &= Value Operação AND e atribuição
Lvalue |= Valor OR e atribuir
Lvalue ^= Valor XOR e atribuir

Avaliação

Operador Significado
Valor ? Valor : Valor Avaliação condicional
Valor , Valor Avaliar todos os valores e descartar todos, exceto o valor mais à direita

Macros em expressões C++

Você pode usar macros em expressões C++. Você deve adicionar um sinal de número (#) antes das macros.

Você pode usar as macros a seguir. Essas macros têm as mesmas definições que as macros do Microsoft Windows com o mesmo nome. As macros do Windows são definidas em Winnt.h.

Macro Valor de retorno
#CONTAINING_RECORD(Endereço, Tipo, Campo) Retorna o endereço base de uma instância de uma estrutura, dado o tipo da estrutura e o endereço de um campo dentro da estrutura.
#FIELD_OFFSET(Tipo, Campo) Retorna o deslocamento de bytes de um campo nomeado em um tipo de estrutura conhecido.
#RTL_CONTAINS_FIELD(Struct, Size, Field) Indica se o tamanho do byte determinado inclui o campo desejado.
#RTL_FIELD_SIZE(Tipo, Campo) Retorna o tamanho de um campo em uma estrutura de tipo conhecido, sem exigir o tipo do campo.
#RTL_NUMBER_OF(Array) Retorna o número de elementos em um arranjo de tamanho estático.
#RTL_SIZEOF_THROUGH_FIELD(Tipo, Campo) Retorna o tamanho de uma estrutura de tipo conhecido, até e incluindo um campo especificado.

Este exemplo mostra o uso da #FIELD_OFFSET macro para calcular o deslocamento de bytes para um campo em uma estrutura.

0:000> ?? #FIELD_OFFSET(_PEB, BeingDebugged)
long 0n2

Consulte também

Expressões MASM versus expressões C++

?? avaliar expressão C++

? avaliar expressão

.expr escolher avaliador de expressão

Extensão de sinal

Exemplos de expressão mista