ポインター演算子を使うと、変数のアドレスの取得 (&)、ポインターの逆参照 (*)、ポインター値の比較、ポインターと整数の加算と減算を行うことができます。
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 項 * 演算子では、数値オペランドの積が計算されます。
ポインター メンバー アクセス演算子 ->
-> 演算子では、ポインターの間接参照とメンバー アクセスが組み合わされます。
xがT*型のポインターであり、yがT型のアクセス可能なメンバーである場合、フォームの式
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値は、int、uint、long、または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と、int、uint、long、またはulongに暗黙的に変換できる型の式nの場合、加算と減算は次のように機能します。
-
p + nとn + 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 つのポインターp1とp2の場合、式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# 言語仕様」の次のセクションを参照してください。
関連項目
.NET