Поделиться через


Номера и операторы C++

В этой статье описывается использование синтаксиса выражений C++ с средствами отладки Windows.

Отладчик принимает два различных типа числовых выражений: выражения C++ и выражения сборщика макросов Майкрософт (MASM). Каждое из этих выражений соответствует собственным правилам синтаксиса входных и выходных данных.

Дополнительные сведения об использовании каждого типа синтаксиса см. в статье "Оценка выражений" и команда "Оценить выражение".

Средство синтаксического анализа выражений C++ поддерживает все формы синтаксиса выражений C++. Синтаксис включает все типы данных, включая указатели, числа с плавающей запятой и массивы, а также все унарные и двоичные операторы C++.

Окна "Контрольные " и "Локальные" в отладчике всегда используют средство оценки выражений C++.

В следующем примере команда ?? evaluate C++ expression отображает значение регистра указателя команд.

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

Для определения размера структур можно использовать функцию C++ sizeof .

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

Задайте для вычислителя выражений значение C++

Используйте оценщик .expr choose expression, чтобы увидеть оценщик выражений по умолчанию и изменить его на C++.

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

После изменения вычислителя выражений по умолчанию можно использовать команду для отображения выражений C++. В следующем примере отображается значение регистра указателя инструкции.

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

Дополнительные сведения о ссылке на регистр @eip см. в разделе Синтаксис регистра.

В этом примере шестнадцатеричное значение 0xD добавляется в регистр eip.

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

Регистры и псевдорегистры в выражениях C++

В выражениях C++ можно использовать регистры и псевдорегистры. Перед регистрацией или псевдорегистром необходимо добавить знак @.

Средство оценки выражений автоматически выполняет правильный приведение. Фактические регистры и псевдорегистры с целочисленным значением приводятся к ULONG64. Все адреса приведены к PUCHAR, $thread приведен к ETHREAD*, $proc приведен к EPROCESS*, $teb приведен к TEB*, и $peb приведен к PEB*.

В этом примере отображается 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

Нельзя изменить регистр или псевдорегистр с помощью оператора присваивания или побочного эффекта. Для изменения этих значений необходимо использовать команду r registers .

В следующем примере устанавливается псевдорегистр на значение 5, после чего это значение отображается.

0:000> r $t0 = 5

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

Дополнительные сведения о регистрах и псевдорегистрирующих регистрах см. в разделе Register синтаксис и синтаксис псевдорегистрирующего регистра.

Числа в выражениях C++

Числа в выражениях C++ интерпретируются как десятичные числа, если они не указаны другим способом. Чтобы указать шестнадцатеричное целое число, добавьте 0x перед числом. Чтобы указать восьмеричное целое число, добавьте 0 (ноль) перед числом.

Радикс отладчика по умолчанию не влияет на способ ввода выражений C++. Вы не можете напрямую ввести двоичное число, кроме как когда вы вкладываете выражение MASM в выражение C++.

Шестнадцатеричное 64-разрядное значение можно ввести в формате xxxx'xxxx. Вы также можете опустить серьезный акцент ('). Оба формата создают одно и то же значение.

Можно использовать суффиксы L, U и I64 с целыми значениями. Фактический размер создаваемого числа зависит от суффикса и введенного числа. Дополнительные сведения об этой интерпретации см. в справочнике по языку C++.

Выходные данные вычислителя выражений C++ сохраняют тип данных, указанный правилами выражений C++. Однако при использовании этого выражения в качестве аргумента для команды всегда выполняется приведение. Например, вам не нужно приведение целых значений к указателям, когда они используются в качестве адресов в аргументах команд. Если значение выражения не может быть допустимо приведено к целому числу или указателю, возникает синтаксическая ошибка.

Префикс 0n (десятичный) можно использовать для некоторых выходных данных, но он не подходит для ввода выражений на C++.

Символы и строки в выражениях C++

Вы можете ввести символ, окружив его одними кавычками ('). Разрешены стандартные escape-символы C++.

Строковые литералы можно вводить, окружая их двойными кавычками ("). Вы можете использовать \" в качестве escape-последовательности в такой строке. Однако строки не имеют значения для вычислителя выражений.

Символы в выражениях C++

В выражении C++ каждый символ интерпретируется в соответствии с типом. В зависимости от того, к чему относится символ, он может интерпретироваться как целое число, структура данных, указатель функции или любой другой тип данных. Синтаксическая ошибка возникает, если используется символ, который не соответствует типу данных C++ ( например, немодифицированному имени модуля) в выражении C++ .

Вы можете использовать серьезный акцент (') или апостроф (') в имени символа, только если добавить имя модуля и восклицательный знак перед именем символа. При добавлении разделителей < и > после имени шаблона можно добавлять пробелы между этими разделителями.

Если символ может быть неоднозначным, можно добавить имя модуля и восклицательный знак (!) или только восклицательный знак перед символом. Чтобы указать, что символ должен быть локальным, опустить имя модуля и включить знак доллара и восклицательный знак ($!) перед именем символа. Дополнительные сведения о распознавании символов см. в разделе "Синтаксис символов" и "Сопоставление символов".

Структуры в выражениях C++

Оценщик выражений C++ приводит псевдорегистры к соответствующим типам. Например, $teb преобразуется в 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

В следующем примере отображается идентификатор процесса в структуре TEB, показывающий использование указателя на элемент ссылочной структуры.

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

Операторы в выражениях C++

Скобки можно использовать для переопределения правил приоритета.

Если вы заключаете часть выражения C++ в скобки и добавляете два знака (@@) перед выражением, выражение интерпретируется в соответствии с правилами выражений MASM. Нельзя добавить пробел между двумя знаками at и открывающей скобкой. Окончательное значение этого выражения передается в средство оценки выражений C++ в качестве значения ULONG64. Вы также можете указать средство оценки выражений с помощью @@c++( ... ) или @@masm( ... ).

Типы данных указываются как обычные на языке C++. Символы, указывающие массивы ([ ]), элементы указателя (->), элементы UDT (.) и члены классов (::) распознаются. Поддерживаются все арифметические операторы, включая операторы присваивания и операторы с побочными эффектами. Однако вы не можете использовать операторы new, delete и throw, и фактически не можете вызвать функцию.

Арифметика указателя поддерживается и смещения масштабируются правильно. Обратите внимание, что невозможно добавить смещение в указатель функции. Если необходимо добавить смещение в указатель функции, сначала приведите смещение к указателю символа.

Как и в C++, при использовании операторов с недопустимыми типами данных возникает синтаксическая ошибка. Средство синтаксического анализа выражений C++ отладчика использует немного более расслабленные правила, чем большинство компиляторов C++, но все основные правила применяются. Например, нельзя сместить значение, не являющееся целым числом.

Можно использовать следующие операторы. Операторы в каждой ячейке имеют приоритет над операторами в нижних ячейках. Операторы в одной ячейке имеют одинаковый приоритет и анализируются слева направо.

Как и в C++, оценка выражений заканчивается, когда его значение известно. Это окончание позволяет эффективно использовать такие выражения, как ?? myPtr && *myPtr.

Приведение ссылочных и типовых данных

Оператор Значение
Выражение // Комментарий Игнорировать весь последующий текст
Класс :: Член Член класса
Класс ::~Элемент Член класса (деструктор)
:: Имя Глобальный
Структура . Поле Поле в структуре
Указатель ->Поле Поле в указанной структуре
Имя [целое число] Подстрочный индекс массива
LValue ++ Инкремент (после вычисления)
LValue -- Декремент (после оценки)
dynamic_cast<тип>(Значение) Typecast (всегда выполняется)
static_cast<тип>(Значение) Typecast (всегда выполняется)
reinterpret_cast<тип>(Значение) Typecast (всегда выполняется)
const_cast<тип>(Значение) Typecast (всегда выполняется)

Операции со значениями

Оператор Значение
(тип) Ценность Typecast (всегда выполняется)
sizeofзначение Размер выражения
sizeof( тип ) Размер типа данных
++ LValue Инкремент (перед оценкой)
-- LValue Декремент (перед оценкой)
~ Ценность Битовое дополнение
! Ценность НЕ (Boolean)
Ценность Унарный минус
+ Ценность Унарный плюс
& LValue Адрес типа данных
Ценность Разыменовать
Структура . указателя Указатель на элемент структуры
Указатель -> * Указатель Указатель на элемент ссылочной структуры

Арифметика

Оператор Значение
Значение Умножение
Ценность / Ценность Подразделение
Ценность % Ценность Модуль
Ценность + Ценность Дополнение
Ценность - Ценность Вычитание
Ценность<<Ценность Сдвиг влево по битам
Ценность>>Ценность Побитовый сдвиг вправо
Ценность<Ценность Меньше (сравнение)
Ценность<= значение Меньше или равно (сравнение)
Ценность>Ценность Больше, чем (сравнение)
Ценность>= значение Больше или равно (сравнение)
Ценность == Ценность Равно (сравнение)
Значение != Значение Не равно (сравнение)
Значение и Значение Битовое И
Ценность ^ Ценность Побитовое XOR (исключающее ИЛИ)
Ценность | Ценность Битовое ИЛИ
Значение && Значение Логическое AND
Ценность || Ценность Логическое ИЛИ

В следующих примерах предполагается, что псевдорегистры установлены, как показано ниже.

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

Задание

Оператор Значение
LValue = Ценность Назначать
LValue *= Ценность Умножить и присвоить
LValue /= Ценность Деление и назначение
LValue %= Ценность Модуло и назначение
LValue += Ценность Добавление и назначение
LValue -= Ценность Вычитание и назначение
LValue<<= значение Смена влево и назначение
LValue>>= значение Сдвиг вправо с присваиванием
LValue &= Value И и назначение
LValue |= Ценность ИЛИ и назначение
LValue ^= Ценность XOR и присвоение

Оценка

Оператор Значение
Значение ? Значение : Значение Условная оценка
Значение , Значение Оценка всех значений, а затем отмена всех, кроме самого правого значения

Макросы в выражениях C++

Макросы можно использовать в выражениях C++. Перед макросами необходимо добавить знак номера (#).

Можно использовать следующие макросы. Эти макросы имеют те же определения, что и макросы Microsoft Windows с тем же именем. Макросы Windows определены в Winnt.h.

Макрос Возвращаемое значение
#CONTAINING_RECORD(адрес, тип, поле) Возвращает базовый адрес экземпляра структуры, учитывая тип структуры и адрес поля в структуре.
#FIELD_OFFSET(Тип, поле) Возвращает смещение байтов именованного поля в известном типе структуры.
#RTL_CONTAINS_FIELD(структура, размер, поле) Указывает, включает ли заданный размер байтов требуемое поле.
#RTL_FIELD_SIZE(Тип, поле) Возвращает размер поля в структуре известного типа, не требуя типа поля.
#RTL_NUMBER_OF(Массив) Возвращает количество элементов в массиве статического размера.
#RTL_SIZEOF_THROUGH_FIELD(Тип, поле) Возвращает размер структуры известного типа, вплоть до указанного поля.

В этом примере показано использование #FIELD_OFFSET макроса для вычисления смещения байтов в поле в структуре.

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

См. также

Выражения MASM и выражения C++

?? оценка выражения C++

? вычислять выражение

.expr выберите средство оценки выражений

Расширение для подписи

Примеры смешанных выражений