C# でのポインタの使用

C# ではポインタの使用は推奨されていないが,C/C++ コードとの連携や効率化のためにどうしても必要になることがある.このために,C# はいくつかの制限事項はありながらもポインタの使用を可能にしている.ここでは,C# で実際にポインタを使ってみる.

unsafe キーワード

C# にてポインタを使用する場合,「この範囲でポインタを使用します.」ということを明示的に宣言しなくてはいけない.この宣言を行うのが unsafe キーワードである.unsafe キーワードの実際の使用例は下記のとおり.

// 1. 関数の宣言時に.

[DllImport("", SetLastError=true)]
static extern unsafe bool ReadFile(int hFile, void* lpBuffer, int nBytesToRead, int* nBytesRead, int overlapped);

// 2. コードの決まったブロックの中で.

static void Main() {

  int num = 10;

  // Unsafe ブロック
  unsafe {
    int* p;
    p = #
    Console.Writeline("Address of Num : {0:X}", (UInt64)p);

  }
  return;
}

fixed キーワード

ポインタで変数を参照・更新するということは,対象となる変数がメモリ上の同じ場所にあることが保証されなくてはいけない.(勝手にアドレスが変わってしまうと困る.)しかしながら,.NET Framework では,ガーベッジコレクションが常にバックグラウンドで動いているために,知らず知らずのうちに対象となる変数・バッファのアドレスが変更されていることがありうる.これを防ぐために,C# では fixed キーワードを用いて対象となる変数を固定することができる.fixed キーワードによって明示的に宣言されたアドレスに関しては,ガーベッジコレクタはメモリを変更しない.
※ Fixed キーワードを用いて変数をメモリに固定化すると,一般的には .NET の効率を下げてしまう.そのため,本当に必要なときに,必要な場所で,できるだけ小さい範囲で fixed キーワードを用いるべきである.

  public unsafe int Read(byte[] buffer, int index, int count) {

    int bytesRead = 0;
    // Fixed スコープ
    // アドレスを取得したインスタンスは格納先のメモリ領域が移動・削除されなくなり,アドレスが変化しないことが保証される.
    fixed(byte* bytePointer = buffer) 
    {
      ReadFile(fileHandle, bytePointer + index, count, &bytesRead, 0);
    }
    return bytesRead;
  }