C++ 数字和运算符

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

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

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

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

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

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

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

可以使用 C++ sizeof 函数来确定结构的大小。

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

将表达式计算器设置为 C++

使用 .expr choose 表达式 计算器查看默认表达式计算器并将其更改为 C++。

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

更改默认表达式计算器后,可以使用 ? evaluate expression 命令来显示 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*EPROCESS*$proc强制转换为 、$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++ 表达式的方式。 不能直接输入二进制数,除非在 C++ 表达式中嵌套 MASM 表达式。

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

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

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

可以将 0n (十进制) 前缀用于某些 输出,但不能将其用于 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 表达式规则解释该表达式。 不能在两个 at 符号和左括号之间添加空格。 此表达式的最终值作为ULONG64值传递给 C++ 表达式计算器。 还可以使用 @@c++( ... )@@masm( ... )指定表达式计算器。

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

支持指针算术,并正确缩放偏移量。 请注意,不能向函数指针添加偏移量。 如果必须向函数指针添加偏移量,请先将偏移量强制转换为字符指针。

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

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

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

引用和类型强制转换

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

值操作

运算符 含义
(类型) 始终) 执行 Typecast (
sizeof 表达式的大小
sizeof ( 类型 ) 数据类型的大小
++ LValue 在评估) 之前递增 (
-- LValue 评估) 之前的递减 (
~ Value 位补码
! 不 (布尔)
一元负
+ Value 一元加
& LValue 数据类型的地址
Dereference
结构指针 指向 结构成员的指针
指针 -> * 指针 指向所引用结构的成员的指针

算术

运算符 含义
乘法
价值 / 价值 部门
价值 % 价值 Modulus
价值 + 价值 加法
价值 - 价值 减法
价值<<价值 按位左移
价值>>价值 按位向右移
价值<价值 小于 (比较)
价值<= 小于或等于 (比较)
价值>价值 大于 (比较)
价值>= 大于或等于 (比较)
价值 == 价值 相等 (比较)
Value != Value 不等于 (比较)
& 位与
价值 ^ 价值 按位异或 (异或)
价值 | 价值 按位“或”
&& 逻辑与
价值 || 价值 逻辑或

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

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

计算

运算符 含义
条件计算
ValueValue 计算所有值,然后丢弃除最右侧值以外的所有值

C++ 表达式中的宏

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

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

返回值
#CONTAINING_RECORD (地址、类型、字段) 返回结构实例的基址,给定结构的类型和结构内字段的地址。
#FIELD_OFFSET (类型,字段) 返回已知结构类型中命名字段的字节偏移量。
#RTL_CONTAINS_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 表达式计算器

签名扩展

混合表达式示例