C#でポインタは使えるの?unsafeの使い方と基礎知識まとめ


ポインタってなに? どうやって扱うの?
参照情報自体を表示したい

こんにちは、エンジニアでC#ライターの遠藤です!

CC++で目にする事のあったポインタは、C#でも存在しています。今回の記事では、C#におけるポインタの基礎知識と使い方について紹介します。

この記事は以下のような方へ向けて書きました。

  • ポインタについて知りたい方
  • C#でポインタが使いたい方

この記事を読んでいただけると、基礎的な知識と使い方が分かります。是非最後までお付き合いください。

目次

ポインタとは

コーディングする中で宣言する変数の値は、メモリに保存されます。そのさいメモリには値を格納する領域があり、そこにはそれぞれアドレスが付けられています。

そのアドレスそのものの値を示す事ができるのがポインタです。

ポインタの基本的な使い方は後ほど解説しますが、基本としては変数に「*(アスタリスク)」をつけたものがポインタとなります。

実際の例:

using System;

class Program
{
    public static void Main()
    {
        unsafe //unsafeブロックの宣言
        {
            int* p; //ポインタの宣言
            int n = 10;

            p = &n; //pにnのアドレスを代入している
            Console.WriteLine((int)p);
        }
    }
}

実行結果:

1735908748

※割り振られるアドレスは都度違うので、実行結果は毎回変わります。

ここで一つ注意していただきたいのが、C#ではポインタはCやC++などとは異なり、重大なバグの原因になりやすいことなどから原則としてポインタを使う事は制限されています

C言語との連携などでどうしても使用したいときは「unsafe」と記述されたブロックの中でだけ使用可能で、くわえてコンパイルのときに/unsafeオプションをつける事が条件となります。

unsafeとは

上述の通り、C#ではポインタをどこでも使えるという訳では無く、unsafeが記述されたブロックだけで使用可能です。

ここではunsafeブロックの書き方を紹介します。

unsafeの使い方

unsafeの基本的な使い方は先ほどの例の通りで、「unsafe{処理}」と記述します。

この処理の中でだけポインタが書けます。

using System;

class Program
{
    public static void Main()
    {
        unsafe //unsafeブロックの宣言
        {
            int* p; //ポインタの宣言
            int n = 10;

            p = &n; //pにnのアドレスを代入している
            Console.WriteLine((int)p);
        }
    }
}

unsafe修飾子

unsafeはブロックとするだけでなく、クラスやメソッドをunsafeとすることも可能です。

その時は、unsafe修飾子を付けます。

実際の例:

using System;

class Program
{
    unsafe void UnsafeMethod()
    {
        int* p; //ポインタの宣言
        int n = 10;

        p = &n; //pにnのアドレスを代入している
        Console.WriteLine(*p); //pのアドレスに格納されている値を出力
    }
    public static void Main()
    {
        Program test = new Program();
        test.UnsafeMethod();
    }
}

実行結果:

10

ポインタの使い方

前章のサンプルコードを再度ご確認ください。

11行目で値を出力する際、「*p」と記述されていることが分かりますね。*pは、pのアドレスに格納されている値を意味しています。

ポインタには他とは少し違う操作が必要になりますので、以下の一覧を見て基本的な操作を把握しておきましょう。

演算子意味備考
*アドレスに格納されている値を参照する(間接参照)
->ポインタからその構造体のメンバにアクセスするa->bは(*a).bと同じ意味
[]ポインタにインデックスを付ける
&変数のアドレスを取得する
++ポインタのインクリメント
--ポインタのデクリメント
+, - ポインタ演算をする
==、!=、<、>、<=、>=ポインタを比較する

fixedステートメントについて

C#にはメモリを整理する処理でガベージコレクション(以降GC)、コンパクションというものがあります。GCが発生すると、その前後でオブジェクトの配置場所が変更されてしまいます。

配置場所が変わってしまうと、当然アドレスが違うので参照ができなくなってしまいます。

これでは困るので、GCでオブジェクトの配置場所が変わってしまうリスクが伴う場合はfixedステートメントを付ける必要が出てきます。

実際の例:

using System;
using System.Runtime.CompilerServices;

class DummyClass
{
    public int x;
    public int y;
}
class Program
{
    unsafe void UnsafeMethod()
    {
        DummyClass test = new DummyClass();
        
        fixed(int* p = &test.x)
        {
            //無理やりGCを行う
            for (int i = 0; i < 99999999; i++) { var obj_dummy = new object(); }
            Console.WriteLine("pのアドレス: " + (int)p);
            Console.WriteLine("GC前のメモリ: " + GC.GetTotalMemory(false));

            GC.Collect(0, GCCollectionMode.Forced);
            Console.WriteLine("+++GCを行います+++");

            Console.WriteLine("pのアドレス: " + (int)p);
            Console.WriteLine("GC後のメモリ: " + GC.GetTotalMemory(false));
        }
    }
    public static void Main()
    {
        Program test = new Program();
        test.UnsafeMethod();
    }
}

実行結果:

pのアドレス: -2092473304
GC前のメモリ: 3242112
+++GCを行います+++
pのアドレス: -2092473304
GC後のメモリ: 119232

GCが発生しても、fixedステートメント内ではアドレスが固定されている事が分かります。

まとめ

今回の記事では、以下のことを解説致しました。

  • ポインタとは
  • unsafeとは
  • ポインタの使い方
  • fixedステートメントについて

ポインタは基本的にNGですが、どうしても必要になったケースでは"使わざるを得ない"状態になっていることと思います。

別の方法がなければ覚えるしかないので、ここでC#におけるポインタの基礎的な使い方を覚えて役立ててください。

この記事を書いた人

【プロフィール】
DX認定取得事業者に選定されている株式会社SAMURAIのマーケティング・コミュニケーション部が運営。「質の高いIT教育を、すべての人に」をミッションに、IT・プログラミングを学び始めた初学者の方に向け記事を執筆。
累計指導者数4万5,000名以上のプログラミングスクール「侍エンジニア」、累計登録者数1万8,000人以上のオンライン学習サービス「侍テラコヤ」で扱う教材開発のノウハウ、2013年の創業から運営で得た知見に基づき、記事の執筆だけでなく編集・監修も担当しています。
【専門分野】
IT/Web開発/AI・ロボット開発/インフラ開発/ゲーム開発/AI/Webデザイン

目次