Привет всем, в данной статье я приведу несколько способов создания указателей в C#.
Указатели необходимы к примеру ,для взаимодействия с библиотеками, использующими ручное выделение/удаление блоков памяти.
Способы создания указателей | ||
stackalloc | fixed | Marshal |
Первым из них является stackalloc.
Выражение
stackalloc
выделяет блок памяти в стеке. Выделенный в стеке блок памяти, который создает этот метод, автоматически удаляется по завершении выполнения метода. Вы не можете явным образом освободить память, выделеннуюstackalloc
. Выделенный в стеке блок памяти не подвергается сборке мусора
Ниже приведу фрагмент кода, создающего указатель с помощью stackalloc.
private static void Malloc(int Length)
{
unsafe
{
bool* n = stackalloc bool[Length];
}
}
Однако, стоит отметить, что выделение памяти имеет ограничение.
Так, попытка выделения в 64битном приложение , массива bool длиной 5 мегабайт (5 242 880 байт), создала StackOverflowExection .
В специфекации stackalloc ,отсутствует максимальный объем выделяемой памяти, но говорится следующее :
Объем доступной памяти в стеке ограничен. При выделении слишком большого объема памяти в стеке возникает исключение StackOverflowException.
…
Поскольку объем доступной памяти на стеке зависит от среды, в которой выполняется код, при определении фактического предельного значения следует использовать консервативное значение. Старайтесь не использовать
stackalloc
в циклах. Выделяйте блок памяти за пределами цикла и используйте его повторно внутри цикла.
В целом, stackalloc , полезен для ситуаций , когда не требуется выделять большой блок памяти.
Следующим способом создания указателя , является fixed .
Оператор
fixed
задает указатель на управляемую переменную и "закрепляет" эту переменную во время выполнения оператора. Указатели на перемещаемые управляемые переменные полезны только в контекстеfixed
. Без контекстаfixed
при сборке мусора эти переменные могут переноситься непредсказуемым образом. Компилятор C# позволяет присвоить указатель только управляемой переменной в оператореfixed
.Оператор
fixed
не позволяет сборщику мусора переносить перемещаемую переменную. Операторfixed
допускается только в небезопасном контексте. Можно также использовать ключевое словоfixed
для создания буферов фиксированного размера.
Ниже приведу фрагмент кода , использующего оператор fixed для создания указателя :
static void Copy(int[]m,out int[]n,int offset,int Length)
{
n = new int[Math.Min(m.Length - offset, Length)];
unsafe
{
fixed(int* _m = m, _n = n)
{
Copy(_m, _n, offset, n.Length);
}
}
}
static unsafe void Copy(int *_inp,int *_out,int offset,int Length)
{
Parallel.For(0, Length, r => {
_out[r] = _inp[r + offset];
});
}
Сразу отмечу, что к указателю , созданному в блоке fixed, нельзя получить доступ из Parallel.For. Также, указатель созданный в блоке fixed, нельзя приравнивать к другому указателю. Если указатели относятся к 1 типу данных, их можно поместить в 1 оператор fixed.
Ещё одним способом создания указателя является библиотечный класс Marshal. Данный класс находится в пространстве имен System.Runtime.InteropServices.
Предоставляет коллекцию методов для выделения неуправляемой памяти, копирования блоков неуправляемой памяти и преобразования управляемых типов в неуправляемые, а также прочих разнообразных методов, используемых при взаимодействии с неуправляемым кодом.
В данном классе присутствуют 2 метода, позволяющих выделять память под указатели.
Освобождает память, выделенную ранее из неуправляемой памяти процесса. | Выделяет память из неуправляемой памяти процесса, используя заданное количество байтов. |
Освобождает блок памяти, выделенный неуправляемым механизмом распределения памяти для задач COM. | Выделяет блок памяти указанного размера из механизма распределения памяти для задач COM. |
В случае использования AllocHGlobal и AllcCoTaskMem выделенную память необходимо очистить с помощью методов FreeHGlobal и FreeCoTaskMem соответственно. AllocHGlobal способен выделить до 2 гигабайт памяти, AllcCoTaskMem в отличии от него, способен работать и с большим объемом.
Всем спасибо за внимание.