サーチ…
備考
ポインタとunsafe
その性質上、ポインタは検証不能なコードを生成します。したがって、ポインタ型を使用するには、 unsafe
コンテキストが必要です。
System.IntPtr
型は、 void*
囲む安全なラッパーです。これは、安全ではないコンテキストが他のタスクを手作業で実行する必要がない場合、 void*
より便利な代替として意図されています。
未定義の動作
CやC ++と同様に、ポインタの不正使用は未定義のビヘイビアを呼び出す可能性があり、メモリ破壊や意図しないコードの実行が副作用の可能性があります。ほとんどのポインタ操作の確認不可能な性質のため、ポインタの正しい使用法はプログラマの責任です。
ポインタをサポートする型
CやC ++とは異なり、すべてのC#型に対応するポインタ型があるわけではありません。以下の基準の両方が適用される場合、タイプT
は対応するポインタタイプを有することができる。
-
T
は、struct型またはポインタ型です。 -
T
は、これらの基準の両方を再帰的に満たすメンバーのみが含まれます。
配列アクセス用のポインタ
この例は、C#の配列へのCのようなアクセスのためにポインタを使用する方法を示しています。
unsafe
{
var buffer = new int[1024];
fixed (int* p = &buffer[0])
{
for (var i = 0; i < buffer.Length; i++)
{
*(p + i) = i;
}
}
}
unsafe
キーワードは、通常の方法でC#配列にアクセスするときに通常生成される境界チェックをポインタへのアクセスがunsafe
ために必要です。
fixed
キーワードは、例外セーフな方法でオブジェクトをfixed
する命令を発行するようにC#コンパイラに指示します。ガベージコレクタがメモリ内の配列を移動しないようにするには、配列内を指すポインタを無効にするために、ピンで固定する必要があります。
ポインタ演算
ポインターの加算と減算は整数とは異なります。ポインタがインクリメントまたはデクリメントされると、それが指し示すアドレスは参照型のサイズだけ増減します。
例えば、タイプint
(の別名System.Int32
場合)4の大きさを有しているint
アドレス0に格納することができ、その後のint
これにアドレス4に格納され、そしてすることができます。コード内:
var ptr = (int*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 4
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
同様に、タイプlong
(の別名System.Int64
場合)8の大きさを有するlong
アドレス0に格納することができ、その後のlong
ように、アドレス8に格納され、そしてすることができます。コード内:
var ptr = (long*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 16
void
型は特別なものであり、 void
型のポインタも特殊なもので、型がわからないときや重要でないときにcatch-allポインタとして使われます。サイズに依存しない性質のため、 void
ポインタはインクリメントまたはデクリメントできません。
var ptr = (void*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
アスタリスクはタイプの一部です
CおよびC ++では、ポインタ変数の宣言のアスタリスクは、宣言されている式の一部です 。 C#では、宣言のアスタリスクが型の一部です 。
C、C ++、およびC#では、次のスニペットはint
ポインタを宣言しています。
int* a;
CおよびC ++では、次のスニペットはint
ポインタとint
変数を宣言しています。 C#では、2つのint
ポインタを宣言しています。
int* a, b;
CおよびC ++では、次のスニペットは2つのint
ポインタを宣言しています。 C#では無効です:
int *a, *b;
void *
C#は、C ++やC ++から継承し、 void*
型に依存しない、サイズに依存しないポインタとして使用します。
void* ptr;
暗黙の変換を使用して、任意のポインタ型をvoid*
割り当てることができます。
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
逆の場合は、明示的な変換が必要です。
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;
- >を使用したメンバーアクセス
C#がCとC ++シンボルの使用を継承->
型付けされたポインタを介してインスタンスのメンバにアクセスする手段として。
次の構造体を考えてみましょう:
struct Vector2
{
public int X;
public int Y;
}
これは、メンバーにアクセスするための->
の使用例です。
Vector2 v;
v.X = 5;
v.Y = 10;
Vector2* ptr = &v;
int x = ptr->X;
int y = ptr->Y;
string s = ptr->ToString();
Console.WriteLine(x); // prints 5
Console.WriteLine(y); // prints 10
Console.WriteLine(s); // prints Vector2
一般的なポインタ
ポインタをサポートするためにタイプが満たさなければならない基準( 備考参照)は、ジェネリック制約の観点から表現することはできません。したがって、ジェネリック型のパラメーターで指定された型へのポインターを宣言しようとすると失敗します。
void P<T>(T obj)
where T : struct
{
T* ptr = &obj; // compile-time error
}