Поделиться через


выражение 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 во вложенных контекстах в примечании.

См. также