概要
GC
管理下にあるオブジェクトは、アドレスが最適化のために移動されることがあります。詳細は未確認飛行さんのコンパクションの箇所を読むと良いでしょう。
ufcpp.net
unmanaged
ポインター(参照型変数や参照渡し以外)はコンパクションによるアドレスの変更に対応していないので、アドレスを固定させるという操作が必要になってしまいます。そこで利用するのがfixed
ステートメントです。
fixed ステートメントを使うと、ガベージ コレクターによる移動可能変数の再配置を防ぎ、その変数へのポインターを宣言することができます。 固定 (またはピン留め) された変数のアドレスは、そのステートメントの実行中に変わりません。 宣言されたポインターは、対応する fixed ステートメント内でのみ使用できます。 宣言されたポインターは読み取り専用であり、変更できません。
使い方
public struct SampleStruct { public int Value; } public class SampleClass { public int Value; } public static void Main(string[] args) { unsafe { // スタック上に配置されるのでfixedは必要ない int x = 1; int* pX = &x; // 参照型を含まない構造体もスタック上に配置されるのでfixedはいらない SampleStruct sample = new SampleStruct(); SampleStruct* p = &sample; } unsafe { // sampleはヒープに配置されるのでGC管理下 var sample = new SampleClass { Value = 1 }; fixed (int* p = &sample.Value) { // 1 Console.WriteLine(*p); } } unsafe { // 配列もヒープに配置されるのでGC管理下 Span<int> span = Enumerable.Range(0, 10).ToArray().AsSpan(); fixed (int* p = span) { for (var i = 0; i < span.Length; i++) { // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9 Console.WriteLine(p[i]); } } } unsafe { // 文字列もヒープに配置されるのでGC管理下 string message = "Hello!"; fixed (char* p = message) { // H Console.WriteLine(*p); // e // NOTE: 「*(p + 1)」と同じ意味 Console.WriteLine(p[1]); } } }