Небезопасный код
Фрагмент небезопасного кода следует помечать ключевым словом unsafe.
Например:
int i1; unsafe {int *i2=&i1;}
Ключевым словом unsafe требуется обязательно помечать любой фрагмент кода, который содержит указатель.
Модификатор unsafe может быть указан для методов, свойств и конструкторов (за исключением статических конструкторов), а также для блоков кода.
Например:
using System; class Class1 { unsafe static void M1 (int* p) // Небезопасный код: указатель на int { *p *= *p; } // *p - доступ к значению unsafe public static void Main() // Небезопасный код: применен оператор // получения адреса (&) { int i = 4; M1 (&i); Console.WriteLine (i); } }
Чтобы использовать небезопасный код, следует установить опцию компилятора /unsafe. Для этого достаточно выбрать имя проекта в окне Solution Explorer и через контекстное меню вызвать диалогProperty Pages (рис. 18.1) , а затем на странице Configuration Properties | Build установить значение опции Allow unsafe code blocks равным True.
Рис. 18.1. Диалог Property Pages
Указатели можно использовать только с размерными типами, массивами и строками. При задании указателя на массив первый элемент массива должен быть размерного типа.
Выполняющая среда .NET для эффективного управления памятью при удалении одних объектов "перемещает" другие объекты, чтобы исключить фрагментацию памяти свободными блоками памяти. Таким образом, выполняющая среда .NET по умолчанию не гарантирует, что объект, на который получен указатель, всегда будет иметь один и тот же адрес. Для предотвращения перемещения объекта следует использовать оператор fixed, который имеет следующее формальное описание:
fixed ( тип* имя_указателя = выражение ) выполняемый_оператор_или_блок
В качестве типа можно указывать любой неуправляемый тип или void. Выражение является указателем на заданный тип. Фиксация объекта применяется только для указанного выполняемого оператора или блока. Доступ к фиксированной переменной не ограничен областью видимости небезопасного кода.
Поэтому фиксированная переменная может указывать на значение, располагаемое в более широкой области видимости, чем данная область видимости небезопасного кода. При этом компилятор C# не выдает предупреждений о такой ситуации.
Однако компилятор C# не позволит установить указатель на управляемую переменную вне оператора fixed.
Например:
class Point { public int x, y; } class Class1 {[STAThread] static void Main(string[] args) { unsafe { Point pt = new Point(); // pt - это управляемая // переменная fixed ( int* p = &pt.x ) { *p = 1 } // pt.x - // указывает на значение // размерного типа } } }
Одним оператором fixed можно фиксировать несколько указателей, но только в том случае, если они одного типа.
Например:
fixed (byte* pa1 = array1, pa2 = array2) {...}
В том случае, если требуется зафиксировать указатели различных типов, можно использовать вложенные операторы fixed.
Например:
fixed (int* p1 = &p.x) fixed (double* p2 = &array1[5]) { }
Указатели, которые инициализированы оператором fixed, не могут быть изменены. Если объект, на который устанавливается указатель, размещается в стеке (например, переменная типа int), то его местоположение фиксировать не требуется.
Разместить блок памяти в стеке можно и явным образом, используя оператор stackalloc, который имеет следующее формальное описание:
тип * имя_указателя = stackalloc тип [ выражение ];
В качестве типа может быть указан любой неуправляемый тип.
Например:
using System; class Class1 {public static unsafe void Main() { int* a1 = stackalloc int[100]; int* p = a1; // Указатель на первый // элемент массива *p++ = *p++ = 1; for (int i=2; i<100; ++i, ++p) *p = p[-1] + p[-2]; for (int i=0; i<10; ++i) Console.WriteLine (a1[i]); } }
Время жизни указателя, инициализированного с применением stackalloc, ограничено временем выполнения метода, в котором этот указатель объявлен. Инициализировать указатели посредством stackalloc можно только для локальных переменных.