C++ の数値と演算子
この記事では、Windows デバッグ ツールでの C++ 式構文の使用について説明します。
デバッガーは、C++ 式と Microsoft Macro Assembler (MASM) 式という 2 つの異なる種類の数値式を使用できます。 これらの式はそれぞれ、入出力に関する独自の構文規則に従います。
各種類の構文をいつ使用するかについての詳細は、「式の評価」と「? evaluate expression コマンド」を参照してください。
C++ 式パーサーは C++ 式構文のすべての形式をサポートしています。 この構文には、ポインター、浮動小数点数、配列などのすべてのデータ タイプと、すべての C++ 単項演算子および二項演算子が含まれます。
デバッガーの Watch および Locals ウィンドウは常に 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
既定の式エバリュエーターが変更された後、? evaluate expression コマンドを使用して C++ 式を表示できます。 次の例では、命令ポインター レジスタの値を表示しています。
0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02
@eip
レジスタ参照の詳細については、「レジスタの構文」を参照してください。
この例では、16 進値 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++ 式での数値は、別の方法で指定しない限り、10 進数として解釈されます。 16 進整数を指定するには、数値の前に 0x を付けます。 8 進整数を指定するには、数値の前に 0 (ゼロ) を付けます。
既定のデバッガー基数は C++ 式の入力方法には影響しません。 C++ 式内に 2 進数を直接入力することはできませんが、MASM 式を入れ子にすることで入力できます。
16 進数の 64 ビット値は xxxxxxxx`xxxxxxxx 形式で入力できます。 また、バッククォート (`) を省略することもできます。 どちらの形式でも同じ値が得られます。
整数値には、L
、U
、I64
のサフィックスを使用できます。 作成される数値の実際のサイズは、入力するサフィックスと数値によって異なります。 この解釈の詳細については、C++ 言語リファレンスを参照してください。
C++ 式エバリュエーターの出力は、C++ 式規則で指定されたデータ タイプを保持します。 ただし、この式をコマンドの引数として使用する場合は、常にキャストが行われます。 たとえば、整数値をコマンド引数のアドレスとして使用する場合は、整数値をポインターにキャストする必要はありません。 式の値を整数またはポインターに正しくキャストできない場合は、構文エラーが発生します。
0n
(10 進数) プレフィックスは一部の出力には使用できますが、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++ 式の一部を括弧で囲み、式の前に 2 つのアットマーク (@@) を追加すると、その式は MASM の式規則に従って解釈されます。 2 つのアットマークと開始括弧の間にスペースを入れることはできません。 この式の最終値は ULONG64 値として C++ 式エバリュエーターに渡されます。 式エバリュエーターは、@@c++( ... )
または @@masm( ... )
を使用して指定することもできます。
データ タイプは C++ 言語で通常どおりに示されます。 配列 ([ ])、ポインター メンバー (->)、UDT メンバー (.)、クラス メンバー (::) を示す記号はすべて認識されます。 代入演算子や副作用演算子など、すべての算術演算子がサポートされています。 ただし、new
、delete
、および throw
演算子は使用できず、実際に関数を呼び出すこともできません。
ポインター演算がサポートされており、オフセットは正しくスケーリングされます。 ただし、関数ポインターにオフセットを追加することはできません。 関数ポインターにオフセットを追加する必要がある場合は、先にそのオフセットを文字ポインターにキャストします。
C++ と同様、無効なデータ タイプの演算子を使用すると、構文エラーが発生します。 デバッガーの C++ 式パーサーは、ほとんどの C++ コンパイラよりも若干緩い規則を使用しますが、主要な規則はすべて適用されます。 たとえば、整数以外の値をシフトすることはできません。
次の演算子を使用できます。 各セルの演算子は、下位のセルの演算子より優先されます。 同じセルの演算子は同じ優先順位であり、左から右に解析されます。
C++ と同様、式の評価は値が判明した時点で終了します。 この終了により、?? myPtr && *myPtr
のような式を効果的に使用できます。
参照と型キャスト
演算子 | 説明 |
---|---|
Expression // Comment | 後続のテキストをすべて無視する |
クラス :: メンバー | クラスのメンバー |
クラス ::~メンバー | クラスのメンバー (ディストラクター) |
:: 名前 | グローバル |
構造。 フィールド | 構造体内のフィールド |
ポインター ->フィールド | 参照される構造体内のフィールド |
名前 [整数] | 配列インデックス |
左辺値 ++ | インクリメント (評価後) |
左辺値 -- | デクリメント (評価後) |
dynamic_cast<タイプ>(値) | 型キャスト (常に実行) |
static_cast<タイプ>(値) | 型キャスト (常に実行) |
reinterpret_cast<タイプ>(値) | 型キャスト (常に実行) |
const_cast<タイプ>(値) | 型キャスト (常に実行) |
値の操作
演算子 | 説明 |
---|---|
(タイプ) 値 | 型キャスト (常に実行) |
sizeof value | 式のサイズ |
sizeof( タイプ ) | データ タイプのサイズ |
++ 左辺値 | インクリメント (評価前) |
-- 左辺値 | デクリメント (評価前) |
~ Value | ビット補数 |
! Value | 偽 (ブール値) |
Value | 単項マイナス |
+ Value | 単項プラス |
& LValue | データ タイプのアドレス |
Value | Dereference |
構造。 ポインター | 構造体のメンバーへのポインター |
ポインター -> * ポインター | 参照される構造体のメンバーへのポインター |
算術
演算子 | 説明 |
---|---|
値 値 | 乗算 |
値 / 値 | 区分 |
値 % 値 | 剰余 |
値 + 値 | 追加 |
値 - 値 | 減算 |
値<<値 | ビットごとに左シフト |
値>>値 | ビットごとに右シフト |
値<値 | 小なり (比較) |
値<= 値 | 小なりイコール (比較) |
値>値 | 大なり (比較) |
値>= 値 | 大なりイコール (比較) |
値 == 値 | 等価 (比較) |
値 != 値 | 非等価 (比較) |
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 &= Value | AND 代入 |
左辺値 |= 値 | OR と代入 |
左辺値 ^= 値 | 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