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


Числа и операторы C++

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

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

Дополнительные сведения об использовании каждого типа синтаксиса см. в разделе Вычисление выражений и команда ?evaluate expression .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Для некоторых выходных0n данных можно использовать префикс (decimal), но нельзя использовать его для входных данных выражений 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. Нельзя добавить пробел между двумя знаками и открывающей скобкой. Окончательное значение этого выражения передается в средство оценки выражений C++ в качестве значения ULONG64. Вы также можете указать вычислитель выражений с помощью @@c++( ... ) или @@masm( ... ).

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

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

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

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

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

Приведение ссылок и типов

Оператор Значение
Выражение // Комментарий Пропустить весь последующий текст
Класс :: Член Член класса
Класс ::~Member Член класса (деструктор)
:: Имя Глобальный
Структура . Поле Поле в структуре
Указатель ->Поле Поле в структуре, на который указывает ссылка
Имя [целое число] Индекс массива
Lvalue ++ Приращение (после оценки)
Lvalue -- Уменьшение (после оценки)
Dynamic_cast<Тип>(Значение) Typecast (всегда выполняется)
Static_cast<Тип>(Значение) Typecast (всегда выполняется)
Reinterpret_cast<Тип>(Значение) Typecast (всегда выполняется)
< const_castТип>(Значение) Typecast (всегда выполняется)

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

Оператор Значение
(тип) Значение Typecast (всегда выполняется)
sizeofvalue Размер выражения
sizeof( тип ) Размер типа данных
++ Lvalue Приращение (до оценки)
-- Lvalue Уменьшение (до оценки)
~ Значение Битовое дополнение
! Значение Not (boolean)
Значение Унарный минус
+ Значение Унарный плюс
& LValue Адрес типа данных
Значение Dereference
Структура . Указатель Указатель на элемент структуры
Указатель —> * Указатель Указатель на элемент структуры, на который указывает ссылка

Арифметический

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

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

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 &= Значение И и назначение
Lvalue |= Значение ИЛИ и назначение
Lvalue ^= Значение XOR и назначение

Ознакомительная версия

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

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

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

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

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

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

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

См. также раздел

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

?? выражение evaluate C++

? выражение evaluate

.expr choose expression вычислитель

Расширение sign

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