C++数字和运算符

本文介绍如何通过 Windows 调试工具使用 C++ 表达式语法。

调试器接受两种不同类型的数值表达式:C++表达式和Microsoft宏汇编程序(MASM)表达式。 其中每个表达式都遵循其自己的输入和输出语法规则。

有关何时使用每种语法类型的详细信息,请参阅 “计算表达式 ”和 “? 计算表达式 ”命令。

C++表达式分析器支持所有形式的C++表达式语法。 语法包括所有数据类型,包括指针、浮点数和数组,以及所有C++一元运算符和二进制运算符。

调试器中的 “监视 ”和 “局部变量 ”窗口始终使用C++表达式计算器。

在以下示例中, ?计算C++表达式 命令显示指令指针寄存器的值。

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

更改默认表达式计算器后,可以使用 ?evaluate 表达式 命令来显示C++表达式。 以下示例显示指令指针寄存器的值。

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

若要了解有关寄存器参考的详细信息 @eip ,请参阅 Register 语法

在此示例中,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++表达式的方式。 不能直接输入二进制数字,除非在C++表达式中嵌套 MASM 表达式。

可以使用 xxxxxxxx'xxxxxxxx 格式输入十六进制 64 位值。 还可以省略重音符号(`)。 这两种格式都生成相同的值。

可以将LUI64用作带整数值的后缀。 所创建数字的实际大小取决于后缀和输入的数字。 有关此解释的详细信息,请参阅C++语言参考。

C++表达式计算器的 输出 保留C++表达式规则指定的数据类型。 但是,如果将此表达式用作命令的参数,则总是会进行类型转换。 例如,当整数值用作命令参数地址时,不必将其强制转换为指针。 如果表达式的值无法有效强制转换为整数或指针,则会发生语法错误。

可以对某些输出使用 0n (decimal) 前缀,但不能将其用于C++表达式输入。

C++表达式中的字符和字符串

可以通过用单引号(')将字符括起来来输入字符。 允许标准C++转义字符。

可以通过用双引号(")将字符串括起来来输入字符串文本。 可以使用 \“ 作为此类字符串中的转义序列。 但是,字符串对 表达式计算器没有意义。

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 结构中的进程 ID,显示指向所引用结构成员的指针的使用。

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

C++表达式中的运算符

可以使用括号替代优先规则。

如果将C++表达式的一部分括在括号中,并在表达式前添加两个 at 符号(@@),则表达式将按照 MASM 表达式规则进行解释。 不能在两个 “@”符号 和左括号之间添加空格。 此表达式的最终值作为ULONG64值传递给C++表达式计算器。 还可以使用 @@c++( ... )@@masm( ... )指定表达式计算器。

数据类型通常在 C++ 语言中表示。 指示数组([ ])、指针成员(->)、UDT 成员(..)和类成员(::)的符号都将被识别。 支持所有算术运算符,包括赋值和副作用运算符。 但是,不能使用newdeletethrow运算符,并且实际上无法调用函数。

支持指针算术,并正确调整偏移量。 请注意,无法向函数指针添加偏移量。 如果必须向函数指针添加偏移量,请先将偏移量转换为字符指针类型。

与C++一样,如果使用数据类型无效的运算符,则会发生语法错误。 调试器的C++表达式分析程序使用比大多数C++编译器更宽松的规则,但会强制实施所有主要规则。 例如,不能移动非整数值。

可以使用以下运算符。 每个单元格中的运算符优先于较低单元格中的运算符。 同一单元格中的运算符具有相同的优先级,并且从左到右进行分析。

与C++一样,表达式计算在已知值时结束。 此结尾使你能够有效地使用表达式,例如 ?? myPtr && *myPtr

引用和类型强制转换

操作员 含义
表达 // 评论 忽略所有后续文本
:: 成员 类的成员
::~成员 类的成员函数(析构函数)
:: 名称 全球
结构领域 结构中的字段
指针 ->Field 引用结构中的字段
名称 [integer] 数组下标
LValue ++ 递增(评估后)
LValue -- 递减(评估后)
dynamic_cast<类型>( Typecast (始终执行)
static_cast<类型>( 类型转换(始终执行)
reinterpret_cast<类型>( Typecast (始终执行)
< const_cast类型>( Typecast (始终执行)

值操作

操作员 含义
类型价值 类型转换(始终执行)
sizeof 表达式的大小
sizeof类型 数据类型的大小
++ LValue 增量(评估前)
-- LValue 递减(评估前)
~ Value 位补码
! 价值 非(布尔)
价值 一元减
+ Value 一元加
& LValue 数据类型地址
价值 解引用
结构指针 指向结构体成员的指针
指针 -> * 指针 指向所引用结构的成员的指针

算术

操作员 含义
乘法
价值 / 价值 划分
价值 % 价值 模数
价值 + 价值 加法
价值 - 价值
价值<<价值 按位移向左
价值>>价值 按位右移
价值<价值 小于 (比较)
<= 小于或等于 (比较)
价值>价值 大于 (比较)
>= 大于或等于 (比较)
价值 == 价值 相等(比较)
Value != Value 不相等(比较)
& 按位 AND
价值 ^ 价值 按位 XOR (独占 OR)
价值 | 价值 按位 OR
&& 逻辑与
价值 || 价值 逻辑或

以下示例假定伪寄存器已按如下所示进行设置。

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 &= AND 和分配
LValue |= 价值 OR 和分配
LValue ^= 价值 XOR 并赋值

评估

操作员 含义
条件评估
评估所有值,然后放弃除最右侧值以外的所有值

C++表达式中的宏

可以在C++表达式中使用宏。 必须在宏之前添加数字符号(#)。

可以使用以下宏。 这些宏的定义与 Microsoft Windows 中相同名称的宏的定义相同。 Windows 宏定义在 Winnt.h 中。

返回值
#CONTAINING_RECORD(地址、类型、字段 返回结构的实例的基址,给定结构的类型和结构中字段的地址。
#FIELD_OFFSET(Type, Field 返回已知结构类型中命名字段的字节偏移量。
#RTL_CONTAINS_FIELD(结构、大小、字段 指示给定字节大小是否包含所需字段。
#RTL_FIELD_SIZE(Type,Field 返回已知类型结构中字段的大小,而无需字段的类型。
#RTL_NUMBER_OF(Array 返回静态大小的数组中的元素数。
#RTL_SIZEOF_THROUGH_FIELD(Type,Field 返回已知类型的结构大小,包括指定字段。

此示例演示如何使用 #FIELD_OFFSET 宏计算结构中字段的字节偏移量。

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

另请参阅

MASM 表达式与C++表达式

?? 计算C++表达式

? 计算表达式

.expr 选择表达式计算器

签名扩展

混合表达式示例