C# pointer & unsafe

 
型安全性を維持するために、C# はデフォルトではポインターをサポートしませんが、unsafe キーワードを使用してクラスまたはクラスのメンバーを変更すると、そのようなクラスまたはクラスのメンバーは安全でないコードとみなされ、C#安全でないコードでのポインター変数の使用を許可します。共通言語ランタイム (CLR) では、安全でないコードとは検証できないコードです。安全でないコードは必ずしも危険ではありませんが、CLR はコードの安全性を検証できません。したがって、CLR は、信頼されたアセンブリに含まれる安全でないコードのみを実行します。

 

ポインタ変数

C# では、ポインターも変数ですが、その値は別の変数のメモリ アドレスです。ポインターを使用する前に、ポインターを宣言する必要もあります。ポインターを宣言する構文は次のとおりです。

type* var_name;

次の表に、ポインターを定義する例をいくつか示します。

説明する
int*p p は整数へのポインタです
double*p p は double へのポインタです
float*p p は浮動小数点数へのポインタです
int**p p は整数へのポインタへのポインタです
int*[]p p は整数へのポインタの 1 次元配列です。
char*p は文字へのポインタです
void*p p は未知の型へのポインタです

変数の宣言と同じように、次のように 1 行のコードで複数のポインターを宣言することもできます。

int* p1, p2, p3;  

注: ポインタ型はオブジェクトから継承できず、ボックス化とアンボックス化はポインタをサポートしませんが、異なるポインタ型間およびポインタと整数の間の変換は可能です。

[例] 次の例は、C# での安全でないキーワードとポインターの使用を示しています。

 using System;

namespace it-kiso.com
{
    class Demo
    {
        static unsafe void Main(string[] args)
        {
            double f = 3.1415;
            double* p = &f;
            Console.WriteLine("データの内容は: {0} ",  f);
            Console.WriteLine("データのメモリアドレスは: {0}",  (int)p);
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

データの内容は: 3.1415
データのメモリアドレスは: 11530344

ヒント: 上記のコードをコンパイルするときは、コンパイル コマンドに-unsafeを追加する必要があります ( csc -unsafe demo.csなど)。

ポインタを引数として関数に渡します

次の例に示すように、ポインター変数をパラメーターとして関数に渡すことができます。

use System;

namespace it-kiso.com
{
    class Demo
    {
        public unsafe void swap(int* p, int *q)
        {
            int temp = *p;
            *p = *q;
            *q = temp;
        }

        public unsafe static void Main()
        {
            Demo p = new Demo();
            int var1 = 10;
            int var2 = 20;
            int* x = &var1;
            int* y = &var2;

            Console.WriteLine("Swap 関数呼び出し前: var1:{0}, var2: {1}", var1, var2);
            p.swap(x, y);

            Console.WriteLine("Swap 関数呼び出し後: var1:{0}, var2: {1}", var1, var2);
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

Swap 関数呼び出し前:var1:10, var2: 20
Swap 関数呼び出し後: var1:20, var2: 10

ポインタを使用して配列要素にアクセスする

C# では、配列と、配列と同じ名前の配列を指すポインターは、異なる種類のデータです。たとえば、 int* pint[] pは、異なる種類のデータです。ポインタ変数 p の値はメモリ内に固定されていないためインクリメントできますが、配列アドレスはメモリ内に固定されているため、配列 p の値をインクリメントすることはできません。配列データにアクセスするためにポインター変数を使用する必要がある場合は、C または C++ で行うのと同じように、fixed キーワードを使用してポインターを固定できます。例を示してみましょう:

 using System;

namespace it-kiso.com
{
    class Demo
    {
        public unsafe static void Main()
        {
            int[] list = { 10, 100, 200 };
            fixed(int *ptr = list)

            /* ポインター中の配列のアドレスを表示する */
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("list[{0}] のメモリーアドレスは:{1}", i, (int)(ptr + i));
                Console.WriteLine("list[{0}] の値は:{1}", i, *(ptr + i));
            }
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

list[0] のメモリーアドレスは:51981272
list[0] の値は:10
list[1] のメモリーアドレスは:51981276
list[1] の値は:100
list[2] のメモリーアドレスは:51981280
list[2] の値は:200

安全でないコードをコンパイルする

安全でないコードをコンパイルするには、コンパイル時にunsafeコマンドを使用する必要があります。たとえば、安全ではないコードを含む demo.cs プログラムをコンパイルするコマンドは次のとおりです。

csc /unsafe demo.cs

csc -unsafe demo.cs

Visual Studio を使用している場合は、プロジェクトのプロパティで安全でないコードを有効にする必要があります。具体的な手順は次のとおりです。

  • ソリューション エクスプローラーでプロパティ ノードをダブルクリックして、プロジェクトのプロパティを開きます。
  • 「ビルド」タブをクリックします。
  • 「安全でないコードを許可する」オプションを選択します。

「 C# ポインター変数と安全でない」についてわかりやすく解説!絶対に観るべきベスト2動画

【新しいC言語講座】ポインタの基本(1)
【C言語】ポインタがわかりません。教えてください。【プログラミング】
 
型安全性を維持するために、C# はデフォルトではポインターをサポートしませんが、unsafe キーワードを使用してクラスまたはクラスのメンバーを変更すると、そのようなクラスまたはクラスのメンバーは安全でないコードとみなされ、C#安全でないコードでのポインター変数の使用を許可します。共通言語ランタイム (CLR) では、安全でないコードとは検証できないコードです。安全でないコードは必ずしも危険ではありませんが、CLR はコードの安全性を検証できません。したがって、CLR は、信頼されたアセンブリに含まれる安全でないコードのみを実行します。

 

ポインタ変数

C# では、ポインターも変数ですが、その値は別の変数のメモリ アドレスです。ポインターを使用する前に、ポインターを宣言する必要もあります。ポインターを宣言する構文は次のとおりです。

type* var_name;

次の表に、ポインターを定義する例をいくつか示します。

説明する
int*p p は整数へのポインタです
double*p p は double へのポインタです
float*p p は浮動小数点数へのポインタです
int**p p は整数へのポインタへのポインタです
int*[]p p は整数へのポインタの 1 次元配列です。
char*p は文字へのポインタです
void*p p は未知の型へのポインタです

変数の宣言と同じように、次のように 1 行のコードで複数のポインターを宣言することもできます。

int* p1, p2, p3;  

注: ポインタ型はオブジェクトから継承できず、ボックス化とアンボックス化はポインタをサポートしませんが、異なるポインタ型間およびポインタと整数の間の変換は可能です。

[例] 次の例は、C# での安全でないキーワードとポインターの使用を示しています。

 using System;

namespace it-kiso.com
{
    class Demo
    {
        static unsafe void Main(string[] args)
        {
            double f = 3.1415;
            double* p = &f;
            Console.WriteLine("データの内容は: {0} ",  f);
            Console.WriteLine("データのメモリアドレスは: {0}",  (int)p);
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

データの内容は: 3.1415
データのメモリアドレスは: 11530344

ヒント: 上記のコードをコンパイルするときは、コンパイル コマンドに-unsafeを追加する必要があります ( csc -unsafe demo.csなど)。

ポインタを引数として関数に渡します

次の例に示すように、ポインター変数をパラメーターとして関数に渡すことができます。

use System;

namespace it-kiso.com
{
    class Demo
    {
        public unsafe void swap(int* p, int *q)
        {
            int temp = *p;
            *p = *q;
            *q = temp;
        }

        public unsafe static void Main()
        {
            Demo p = new Demo();
            int var1 = 10;
            int var2 = 20;
            int* x = &var1;
            int* y = &var2;

            Console.WriteLine("Swap 関数呼び出し前: var1:{0}, var2: {1}", var1, var2);
            p.swap(x, y);

            Console.WriteLine("Swap 関数呼び出し後: var1:{0}, var2: {1}", var1, var2);
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

Swap 関数呼び出し前:var1:10, var2: 20
Swap 関数呼び出し後: var1:20, var2: 10

ポインタを使用して配列要素にアクセスする

C# では、配列と、配列と同じ名前の配列を指すポインターは、異なる種類のデータです。たとえば、 int* pint[] pは、異なる種類のデータです。ポインタ変数 p の値はメモリ内に固定されていないためインクリメントできますが、配列アドレスはメモリ内に固定されているため、配列 p の値をインクリメントすることはできません。配列データにアクセスするためにポインター変数を使用する必要がある場合は、C または C++ で行うのと同じように、fixed キーワードを使用してポインターを固定できます。例を示してみましょう:

 using System;

namespace it-kiso.com
{
    class Demo
    {
        public unsafe static void Main()
        {
            int[] list = { 10, 100, 200 };
            fixed(int *ptr = list)

            /* ポインター中の配列のアドレスを表示する */
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("list[{0}] のメモリーアドレスは:{1}", i, (int)(ptr + i));
                Console.WriteLine("list[{0}] の値は:{1}", i, *(ptr + i));
            }
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

list[0] のメモリーアドレスは:51981272
list[0] の値は:10
list[1] のメモリーアドレスは:51981276
list[1] の値は:100
list[2] のメモリーアドレスは:51981280
list[2] の値は:200

安全でないコードをコンパイルする

安全でないコードをコンパイルするには、コンパイル時にunsafeコマンドを使用する必要があります。たとえば、安全ではないコードを含む demo.cs プログラムをコンパイルするコマンドは次のとおりです。

csc /unsafe demo.cs

csc -unsafe demo.cs

Visual Studio を使用している場合は、プロジェクトのプロパティで安全でないコードを有効にする必要があります。具体的な手順は次のとおりです。

  • ソリューション エクスプローラーでプロパティ ノードをダブルクリックして、プロジェクトのプロパティを開きます。
  • 「ビルド」タブをクリックします。
  • 「安全でないコードを許可する」オプションを選択します。

「 C# ポインター変数と安全でない」についてわかりやすく解説!絶対に観るべきベスト2動画

【新しいC言語講座】ポインタの基本(1)
【C言語】ポインタがわかりません。教えてください。【プログラミング】