Pull to refresh

Создание указателей в C#

Reading time3 min
Views11K

Привет всем, в данной статье я приведу несколько способов создания указателей в 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 метода, позволяющих выделять память под указатели.

FreeHGlobal(IntPtr)

Освобождает память, выделенную ранее из неуправляемой памяти процесса.

AllocHGlobal(Int32)

Выделяет память из неуправляемой памяти процесса, используя заданное количество байтов.

FreeCoTaskMem(IntPtr)

Освобождает блок памяти, выделенный неуправляемым механизмом распределения памяти для задач COM.

AllocCoTaskMem(Int32)

Выделяет блок памяти указанного размера из механизма распределения памяти для задач COM.

В случае использования AllocHGlobal и AllcCoTaskMem выделенную память необходимо очистить с помощью методов FreeHGlobal и FreeCoTaskMem соответственно. AllocHGlobal способен выделить до 2 гигабайт памяти, AllcCoTaskMem в отличии от него, способен работать и с большим объемом.

Всем спасибо за внимание.

Tags:
Hubs:
Total votes 15: ↑5 and ↓10-5
Comments10

Articles