выражение stackalloc (справочник по C#)
Выражение stackalloc
выделяет блок памяти в стеке. Выделенный стеком блок памяти, созданный во время выполнения метода, автоматически удаляется при возврате этого метода. Вы не можете явно освободить память, выделенную с stackalloc
помощью. Выделенный блок памяти стека не подлежит сборке мусора и не должен быть закреплен с помощью инструкцииfixed
.
Результат выполнения выражения stackalloc
можно присвоить переменной любого из следующих типов:
System.Span<T> или System.ReadOnlySpan<T>, как показано в следующем примере:
int length = 3; Span<int> numbers = stackalloc int[length]; for (var i = 0; i < length; i++) { numbers[i] = i; }
Нет необходимости использовать небезопасный контекст при назначении выделенного в стеке блока памяти переменной Span<T> или ReadOnlySpan<T>.
При работе с такими типами вы можете использовать выражение
stackalloc
в условном выражении или выражении присваивания, как показано в следующем примере:int length = 1000; Span<byte> buffer = length <= 1024 ? stackalloc byte[length] : new byte[length];
Выражение или выражение коллекции можно использовать
stackalloc
в других выражениях всякий раз, когда Span<T> разрешено или ReadOnlySpan<T> переменная, как показано в следующем примере:Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 }; var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6, 8 }); Console.WriteLine(ind); // output: 1 Span<int> numbers2 = [1, 2, 3, 4, 5, 6]; var ind2 = numbers2.IndexOfAny([2, 4, 6, 8]); Console.WriteLine(ind2); // output: 1
Примечание.
Мы рекомендуем везде, где это возможно, использовать для работы с выделенной в стеке памятью типы Span<T> или ReadOnlySpan<T>.
Тип указателя, как показано в следующем примере.
unsafe { int length = 3; int* numbers = stackalloc int[length]; for (var i = 0; i < length; i++) { numbers[i] = i; } }
Как демонстрирует пример выше, при работе с типами указателей необходимо использовать контекст
unsafe
.В случае типов указателей можно использовать выражение
stackalloc
только в объявлении локальной переменной для инициализации переменной.
Объем доступной памяти в стеке ограничен. При выделении слишком большого объема памяти в стеке возникает исключение StackOverflowException. Чтобы избежать этого, следуйте приведенным ниже правилам.
Ограничьте объем памяти, выделенный
stackalloc
. Например, если предполагаемый размер буфера меньше определенного предела, то выделяется память в стеке. В противном случае используйте массив требуемой длины, как показано в следующем коде:const int MaxStackLimit = 1024; Span<byte> buffer = inputLength <= MaxStackLimit ? stackalloc byte[MaxStackLimit] : new byte[inputLength];
Примечание.
Поскольку объем доступной памяти на стеке зависит от среды, в которой выполняется код, при определении фактического предельного значения следует использовать консервативное значение.
Старайтесь не использовать
stackalloc
в циклах. Выделяйте блок памяти за пределами цикла и используйте его повторно внутри цикла.
Содержимое только что выделенной памяти не определено. Его следует инициализировать либо с инициализатором, либо методом stackalloc
, как Span<T>.Clear и раньше.
Внимание
Не инициализация памяти, выделенной по stackalloc
оператору, является важным отличием new
от оператора. Память, выделенная с помощью new
оператора, инициализируется в 0-битовом шаблоне.
Для определения содержимого только что выделенной памяти можно использовать синтаксис инициализатора массива. В следующем примере показано несколько способов сделать это:
Span<int> first = stackalloc int[3] { 1, 2, 3 };
Span<int> second = stackalloc int[] { 1, 2, 3 };
ReadOnlySpan<int> third = stackalloc[] { 1, 2, 3 };
// Using collection expressions:
Span<int> fourth = [1, 2, 3];
ReadOnlySpan<int> fifth = [1, 2, 3];
В выражении stackalloc T[E]
T
должен иметь неуправляемый тип, а E
— неотрицательное значение int. При использовании синтаксиса выражения коллекции для инициализации диапазона компилятор может использовать выделенное хранилище стека для диапазона, если он не нарушает безопасность ссылок.
Безопасность
При использовании stackalloc
в среде CLR автоматически включается контроль переполнения буфера. Если буфер переполнен, процесс незамедлительно прерывается — это позволяет минимизировать риск исполнения вредоносного кода.
Спецификация языка C#
См. сведения о выделении памяти в стеке в спецификации языка C# и разрешении stackalloc
во вложенных контекстах в примечании.