次の方法で共有


ポインター関連演算子 - 変数のアドレスの取得、メモリ位置の逆参照、メモリ位置へのアクセス

ポインター演算子を使うと、変数のアドレスの取得 (&)、ポインターの逆参照 (*)、ポインター値の比較、ポインターと整数の加算と減算を行うことができます。

C# 言語リファレンスには、C# 言語の最新リリース バージョンが記載されています。 また、今後の言語リリースのパブリック プレビューの機能に関する初期ドキュメントも含まれています。

このドキュメントでは、言語の最後の 3 つのバージョンまたは現在のパブリック プレビューで最初に導入された機能を特定します。

ヒント

C# で機能が初めて導入された時期を確認するには、 C# 言語バージョン履歴に関する記事を参照してください。

ポインターを操作するには、次の演算子を使用します。

ポインター型については、「ポインター型」をご覧ください。

注意

ポインターに関するすべての操作には、unsafe コンテキストが必要です。 AllowUnsafeBlocks コンパイラ オプションを使用して、安全でないブロックを含むコードをコンパイルする必要があります。

アドレス取得演算子 &

単項の & 演算子からは、そのオペランドのアドレスが返されます。

unsafe
{
    int number = 27;
    int* pointerToNumber = &number;

    Console.WriteLine($"Value of the variable: {number}");
    Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}
// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4

& 演算子のオペランドは、固定変数である必要があります。 固定 変数は、 ガベージ コレクター が影響を受けないストレージの場所に存在する変数です。 前の例では、 number ローカル変数はスタック上に存在するため、固定変数です。 ガベージ コレクターが影響を受ける可能性があるストレージの場所に存在する変数 (再配置など) は、 移動可能 な変数と呼ばれます。 オブジェクト フィールドや配列の要素は、移動可能変数の例です。 移動可能な変数のアドレスは、 fixed ステートメントを使用して "修正" または "ピン留め" した場合に取得できます。 取得したアドレスは、fixed ステートメントのブロック内でのみ有効です。 fixed ステートメントと & 演算子の使い方の例を次に示します。

unsafe
{
    byte[] bytes = { 1, 2, 3 };
    fixed (byte* pointerToFirst = &bytes[0])
    {
        // The address stored in pointerToFirst
        // is valid only inside this fixed statement block.
    }
}

定数または値のアドレスを取得できません。

固定変数と移動可能変数について詳しくは、「C# 言語仕様」の「Fixed and moveable variables (固定変数と移動可能変数)」セクションをご覧ください。

2 項 & 演算子では、ブール型オペランドの論理 AND または整数オペランドのビットごとの論理 AND が計算されます。

ポインター間接参照演算子 *

単項ポインター間接演算子 * は、そのオペランドが指す変数にアクセスします。 逆参照演算子とも呼ばれます。 * 演算子のオペランドは、ポインター型である必要があります。

unsafe
{
    char letter = 'A';
    char* pointerToLetter = &letter;
    Console.WriteLine($"Value of the `letter` variable: {letter}");
    Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");

    *pointerToLetter = 'Z';
    Console.WriteLine($"Value of the `letter` variable after update: {letter}");
}
// Output is similar to:
// Value of the `letter` variable: A
// Address of the `letter` variable: DCB977DDF4
// Value of the `letter` variable after update: Z

* 型の式に void* 演算子を適用することはできません。

2 項 * 演算子では、数値オペランドのが計算されます。

ポインター メンバー アクセス演算子 ->

-> 演算子では、ポインターの間接参照メンバー アクセスが組み合わされます。 xT*型のポインターであり、yT型のアクセス可能なメンバーである場合、フォームの式

x->y

上記の式は、次の式と同じです。

(*x).y

-> 演算子の使用例を次に示します。

public struct Coords
{
    public int X;
    public int Y;
    public override string ToString() => $"({X}, {Y})";
}

public class PointerMemberAccessExample
{
    public static unsafe void Main()
    {
        Coords coords;
        Coords* p = &coords;
        p->X = 3;
        p->Y = 4;
        Console.WriteLine(p->ToString());  // output: (3, 4)
    }
}

void*型の式に対して->演算子を使用することはできません。

ポインター要素のアクセス演算子 []

ポインター型の式 p の場合、フォーム p[n] のポインター要素のアクセスは *(p + n)として評価されます。 n値は、intuintlong、またはulongに暗黙的に変換できる型である必要があります。 ポインターでの + 演算子の動作については、「ポインターに対する整数値の加算または減算」セクションをご覧ください。

次の例では、ポインターと [] 演算子を使用して配列要素にアクセスする方法を示します。

unsafe
{
    char* pointerToChars = stackalloc char[123];

    for (int i = 65; i < 123; i++)
    {
        pointerToChars[i] = (char)i;
    }

    Console.Write("Uppercase letters: ");
    for (int i = 65; i < 91; i++)
    {
        Console.Write(pointerToChars[i]);
    }
}
// Output:
// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ

前の例では、stackallocによってスタックにメモリ ブロックが割り当てられています。

注意

ポインター要素アクセス演算子では、範囲外のエラーはチェックされません。

[] 型の式でポインター要素アクセスに void* を使うことはできません。

[]には 演算子を使用することもできます。

ポインター算術演算子

ポインターで次の算術演算を実行できます。

  • ポインターに整数値を加算する、またはポインターから整数値を減算する
  • 2 個のポインターを減算する
  • ポインターをインクリメントまたはデクリメントする

void* 型のポインターでこれらの演算を実行することはできません。

数値型を使用してサポートされる算術演算の詳細については、「 算術演算子」を参照してください。

ポインターに整数値を加算する、またはポインターから整数値を減算する

T*型のポインターpと、intuintlong、またはulongに暗黙的に変換できる型の式nの場合、加算と減算は次のように機能します。

  • p + nn + pの両方で、T*型のポインターが提供されます。 このポインターを取得するには、pが指すアドレスにn * sizeof(T)を追加します。
  • p - n式は、T*型のポインターを提供します。 このポインターを取得するには、pが指すアドレスからn * sizeof(T)を減算します。

sizeof演算子は、型のサイズをバイト単位で取得します。

次の例は、ポインターで + 演算子を使用する方法を示しています。

unsafe
{
    const int Count = 3;
    int[] numbers = new int[Count] { 10, 20, 30 };
    fixed (int* pointerToFirst = &numbers[0])
    {
        int* pointerToLast = pointerToFirst + (Count - 1);

        Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");
        Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");
    }
}
// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144

ポインターの減算

T*型の 2 つのポインターp1p2の場合、式p1 - p2では、p1するアドレスとp2ポイントするアドレスの差がsizeof(T)で除算されます。 結果は long 型です。 つまり、 p1 - p2((long)(p1) - (long)(p2)) / sizeof(T)として計算されます。

次の例は、ポインターの減算を示しています。

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };
    int* p1 = &numbers[1];
    int* p2 = &numbers[5];
    Console.WriteLine(p2 - p1);  // output: 4
}

ポインターのインクリメントとデクリメント

++ インクリメント演算子では、ポインター オペランドに 1 が加算されます。 -- デクリメント演算子では、ポインター オペランドから 1 が減算されます。

どちらの演算子も、後置 (p++p--) とプレフィックス (++p--p) の 2 つの形式をサポートしています。 p++ および p-- の結果は、演算の "p" の の値です。 ++p および --p の結果は、演算の "p" の の値です。

次の例では、後置と前置両方のインクリメント演算子の動作を示します。

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2 };
    int* p1 = &numbers[0];
    int* p2 = p1;
    Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");
    Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");
    Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");
    Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");
}
// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516

ポインター比較演算子

==!=<><=>= 演算子を使って、void* を含む任意のポインター型のオペランドを比較できます。 これらの演算子は、2 つのオペランドによって指定されたアドレスを、符号なし整数であるかのように比較します。

他の型のオペランドに対するこれらの演算子の動作については、「等値演算子」および「比較演算子」をご覧ください。

演算子の優先順位

次のポインター関連演算子の一覧は、優先度が高い順に並べられています。

  • 後置インクリメント x++ およびデクリメント x-- 演算子、-> および [] 演算子
  • 前置インクリメント ++x およびデクリメント --x 演算子、& および * 演算子
  • 加法 + および - 演算子
  • 比較 <><=>= 演算子
  • 等値 == および != 演算子

演算子の優先順位によって定められた評価の順序を変更するには、かっこ () を使用します。

優先度順に並べられた C# 演算子の完全な一覧については、C# 演算子に関する記事の「演算子の優先順位」セクションを参照してください。

演算子のオーバーロード可/不可

ポインター関連の演算子を、ユーザー定義型の &*->、および [] にオーバーロードすることはできません。

C# 言語仕様

詳細については、「C# 言語仕様」の次のセクションを参照してください。

関連項目