演算子 | 種類 | 説明 |
---|---|---|
& | address-of演算子 | 他のアドレスへのポインタを返す |
* | dereference演算子 | ポインタのアドレスにある値を返す |
-> | pointer-to-member演算子 | 構文上のショートカット。x->yは(*x).yに相当する |
C#でC言語形式のポインタを使用するためには、次の2点が必要です。
プロジェクトのプロパティで、ビルドにある「アンセーフ コードの許可」にチェックを入れます。これによりコンパイル時に/unsafeオプションが指定され、unsafeキーワードを使用できるようになります。
C言語形式のポインタを使用するためには、unsafeキーワードを使用してアンセーフなコードであることを宣言しなくてはなりません。この宣言はステートメントのブロックに対して行い、そのスコープ内で有効となります。
void Method() { int x; unsafe { int* y = &x; } }
メソッド全体に適用するには、次のようにします。
unsafe void Method() { char* p; }
クラス全体では次のようにします。
unsafe class MyClass { void Method() { char* p; } }
C#ではガベージコレクションによってメモリが自動で管理されています。そこにはメモリの断片化を防ぐためにオブジェクトを移動する機能がありますが、これによってメモリのアドレスが変更されると、そのオブジェクトのポインタは使用できなくなってしまいます。
fixedステートメントを使用すると、マネージド オブジェクトのポインタを固定できます。fixed ステートメント (C# リファレンス) | MSDN
int[] arr = { 1, 2, 3 }; unsafe { fixed (int* p = arr) { Console.Write(*p); // 1 Console.Write(*(p + 1)); // 2 } }
C++/CLIでは、pin_ptrでこれと同様に処理できます。
GCHandleを使用することで、ガベージコレクションを妨げる、アドレスが変更されない固定されたオブジェクトを生成できます。
[SecurityCriticalAttribute] public static GCHandle Alloc( object value, GCHandleType type )GCHandle.Alloc メソッド (Object, GCHandleType) (System.Runtime.InteropServices) | MSDN
typeを省略した書式では、GCHandleType.Normalを指定したものと見なされます。
[SecurityCriticalAttribute] public static GCHandle Alloc( object value )
列挙子 | 目的 | 機能 |
---|---|---|
Weak | オブジェクトの追跡 | オブジェクトがコードから到達不能であることを検出。そのときのオブジェクトの状態は不明 |
WeakTrackResurrection | オブジェクトの追跡 | オブジェクトがコードから到達不能であることを検出。そのときオブジェクトは解放済み。 |
Normal | オブジェクトの寿命の制御 | メモリに残しておくことを指示。ガベージコレクションによって移動される。 |
Pinned | オブジェクトの寿命の制御 | メモリに残しておくことを指示。ガベージコレクションによって移動されない。不要となったときはFree()で解放しなければ、メモリリークを引き起こす。 |
MyClass myClass = new MyClass(); GCHandle handle = GCHandle.Alloc(myClass); IntPtr ptr = GCHandle.ToIntPtr(handle); // IntPtr ptr = (IntPtr)handle; // キャストしても同じ unsafe { void* p = (void*)ptr; // 1行にまとめると、 // void* p = (void*)(IntPtr)GCHandle.Alloc(myClass); } handle.Free();
void*からマネージド オブジェクトへ戻すには、次のようにします。
void* p = (void*)(IntPtr)GCHandle.Alloc(myClass); IntPtr ptr = (IntPtr)p; GCHandle handle = GCHandle.FromIntPtr(ptr); MyClass target = (MyClass)handle.Target; // 1行にまとめると、 // MyClass target = (MyClass)GCHandle.FromIntPtr((IntPtr)p).Target;
マーシャリングを利用することでもvoid*へ変換できます。Void のサンプル | MSDN
[DllImport( "sample.dll" )] public static extern void SetData( DataType t, [MarshalAs(UnmanagedType.AsAny)] object o);
C#でポインタを処理するための構造体です。
可能ならばSafeHandleに置き換えます。CA2006: Use SafeHandle to encapsulate native resources - Visual Studio 2015 | Microsoft Learn
public IntPtr(
int value // 32ビットのポインターまたはハンドル
)
public IntPtr(
long value // 64ビットのポインター
)
[SecurityCriticalAttribute]
[CLSCompliantAttribute(false)]
public unsafe IntPtr(
void* value // 不特定のポインター
)
IntPtr 構造体 (System) | Microsoft Learn
IntPtr ptr = new IntPtr(10); int a = ptr.ToInt32();
リソースへのハンドルをラップできます。この構造体は.NET Framework 2.0以降、SafeHandleやCriticalHandleに置き換えられています。Remarks - HandleRef Struct (System.Runtime.InteropServices) | Microsoft Learn
SafeHandleはIDisposableを実装するため、IntPtrに対する終了処理を記述できます。
MarshalクラスのCopy()メソッドによって、アンマネージド ポインタとマネージド配列で相互にデータをコピーできます。