C++數位和運算符
本文說明搭配 Windows 偵錯工具使用C++表達式語法。
調試程式接受兩種不同類型的數值表達式:C++表達式和Microsoft宏組合器 (MASM) 表達式。 每個表達式都會遵循自己的輸入和輸出語法規則。
如需何時使用每個語法類型的詳細資訊,請參閱 評估表達式 和 ? evaluate expression 命令。
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 expression 命令來顯示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
如需緩存器和虛擬緩存器的詳細資訊,請參閱 註冊語法 和 虛擬緩存器語法。
C++運算式中的數位
除非以其他方式指定數字,否則C++運算式中的數位會解譯為十進位數。 若要指定十六進位整數,請在數位前面加上 0x。 若要指定八進位整數,請在數位前面加上 0 (零)。
默認調試程式基底不會影響您輸入C++表達式的方式。 除了在C++運算式內巢狀 MASM 運算式之外,您無法直接輸入二進位數。
您可以在xxxxxxxxxx'xxxxxxxx格式中輸入十六進位64位值。 您也可以省略墳墓口音 (')。 這兩種格式會產生相同的值。
您可以使用 L
、 U
和 I64
後綴搭配整數值。 所建立數位的實際大小取決於後綴和您輸入的數位。 如需此解譯的詳細資訊,請參閱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 結構中的進程識別碼,其中顯示參考結構成員的指標用法。
0:000> ?? @$teb->ClientId.UniqueProcess
void * 0x0000059c
C++運算式中的運算符
您可以使用括弧來覆寫優先順序規則。
如果您在表達式前面加上括弧中C++表達式的一部分,並在表達式前面加上兩個符號(@@),則表達式會根據MASM運算式規則來解譯。 您無法在符號和左括弧之間新增兩個空格。 此表達式的最終值會傳遞至C++表達式評估工具做為ULONG64值。 您也可以使用 @@c++( ... )
或 @@masm( ... )
指定表示式評估工具。
數據類型會以C++語言一般表示。 表示陣列 ([ ])、指標成員 (->)、UDT 成員 (.) 和類別成員 (::) 的符號全都會被辨識。 支援所有算術運算符,包括指派和副作用運算符。 不過,您無法使用 new
、 delete
和 throw
運算子,而且實際上無法呼叫函式。
支援指標算術,並正確調整位移。 請注意,您無法將位移新增至函式指標。 如果您必須將位移新增至函式指標,請先將位移轉換成字元指標。
如同C++,如果您使用具有無效數據類型的運算符,就會發生語法錯誤。 調試程式的C++表達式剖析器會使用比大多數C++編譯程式稍微寬鬆的規則,但會強制執行所有主要規則。 例如,您無法移動非整數值。
您可以使用下列運算子。 每個儲存格中的運算元優先於較低儲存格中的運算元。 相同儲存格中的運算子的優先順序相同,且會從左至右剖析。
如同C++,表達式評估會在已知其值時結束。 這個結尾可讓您有效地使用 表示式,例如 ?? myPtr && *myPtr
。
參考和型別轉換
運算子 | 意義 |
---|---|
表達式 // 批注 | 忽略所有後續文字 |
類別 :: 成員 | 類別的成員 |
類別 ::~Member | 類別的成員 (解構函式) |
:: 名稱 | 全球 |
結構。 欄位 | 結構中的欄位 |
指標 ->Field | 參考結構中的欄位 |
名稱 [integer] | 陣列註標 |
LValue ++ | 遞增 (評估後) |
LValue -- | 遞減 (評估後) |
<dynamic_cast類型>(值) | Typecast (一律執行) |
<static_cast類型>(值) | Typecast (一律執行) |
<reinterpret_cast類型>(值) | Typecast (一律執行) |
<const_cast類型>(值) | Typecast (一律執行) |
值作業
運算子 | 意義 |
---|---|
(type) 值 | Typecast (一律執行) |
sizeof 值 | 表達式的大小 |
sizeof( 類型 ) | 數據類型的大小 |
++ LValue | 遞增 (評估前) |
-- LValue | 遞減 (評估前) |
~ 值 | 位補碼 |
! 值 | Not (布林值) |
值 | 一元減號 |
+ 值 | 一元加號 |
& LValue | 數據類型的位址 |
值 | Dereference |
結構。 指標 | 結構成員的指標 |
指標 -> * 指標 | 參考結構成員的指標 |
算術
運算子 | 意義 |
---|---|
值 | 乘法 |
值 / | 除法 |
值 % | 模數 |
值 + | 加法 |
值 - | 減法 |
值<< | 位移左 |
值>> | 位向右移位 |
值< | 小於 (比較) |
Value= Value< | 小於或等於 (比較) |
值> | 大於 (比較) |
Value= Value> | 大於或等於 (比較) |
值 == | 相等 (比較) |
Value != Value | 不相等(比較) |
值 和 值 | 位元 AND |
值 ^ | 位 XOR (獨佔 OR) |
值 | | 位元 OR |
Value && Value | 邏輯 AND |
值 || | 邏輯 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++運算式中使用宏。 您必須在宏之前新增數字符號 (#)。
您可以使用下列宏。 這些宏的定義與具有相同名稱的 windows 宏Microsoft相同。 Windows 宏定義於 中 Winnt.h
。
Macro | 傳回值 |
---|---|
#CONTAINING_RECORD(Address, Type, Field) | 傳回 結構實例的基位址,指定 結構的類型和 結構內欄位的位址。 |
#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