espressione stackalloc (riferimenti per C#)

L'espressione stackalloc alloca un blocco di memoria nello stack. Un blocco di memoria allocato nello stack, creato durante l'esecuzione del metodo, viene automaticamente eliminato alla restituzione del metodo. Non è possibile liberare in modo esplicito la memoria allocata con stackalloc. Un blocco di memoria allocato nello stack non è soggetto a una Garbage Collection e non deve essere aggiunto con un’fixedistruzione.

È possibile assegnare il risultato di un’espressione stackalloc a una variabile di uno dei tipi seguenti:

  • System.Span<T> o System.ReadOnlySpan<T>, come illustrato nell'esempio seguente:

    int length = 3;
    Span<int> numbers = stackalloc int[length];
    for (var i = 0; i < length; i++)
    {
        numbers[i] = i;
    }
    

    Non è necessario usare un contesto unsafe quando si assegna un blocco di memoria allocato nello stack a una variabile Span<T> o ReadOnlySpan<T>.

    Se si usano questi tipi, è possibile applicare un'espressione stackalloc in espressioni condizionali o di assegnazione, come illustrato nell'esempio seguente.

    int length = 1000;
    Span<byte> buffer = length <= 1024 ? stackalloc byte[length] : new byte[length];
    

    È possibile usare un'espressione stackalloc o un'espressione di raccolta all'interno di altre espressioni ogni volta che è consentita una variabile Span<T> o ReadOnlySpan<T>, come illustrato nell'esempio seguente:

    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
    

    Nota

    In presenza di memoria allocata nello stack, è consigliabile usare il tipo Span<T> o ReadOnlySpan<T> ogni qualvolta sia possibile.

  • Un tipo di puntatore, come nell'esempio seguente.

    unsafe
    {
        int length = 3;
        int* numbers = stackalloc int[length];
        for (var i = 0; i < length; i++)
        {
            numbers[i] = i;
        }
    }
    

    Come illustrato nell'esempio precedente, quando si usa un tipo di puntatore è necessario adottare un contesto unsafe.

    Nel caso dei tipi di puntatore, è possibile usare un'espressione stackalloc solo in una dichiarazione di variabile locale per inizializzare la variabile.

La quantità di memoria disponibile nello stack è limitata. Se si alloca una quantità eccessiva di memoria nello stack, viene generata un'eccezione StackOverflowException. Per evitare questo problema, attenersi alle regole seguenti:

  • Limitare la quantità di memoria allocata con stackalloc. Ad esempio, se la dimensione prevista del buffer è inferiore a un determinato limite, allocare la memoria nello stack; in caso contrario, usare una matrice della lunghezza richiesta, come illustrato nel codice seguente:

    const int MaxStackLimit = 1024;
    Span<byte> buffer = inputLength <= MaxStackLimit ? stackalloc byte[MaxStackLimit] : new byte[inputLength];
    

    Nota

    Poiché la quantità di memoria disponibile nello stack dipende dall'ambiente in cui viene eseguito il codice, essere conservativi quando si definisce il valore limite effettivo.

  • Evitare di usare cicli interni stackalloc. Allocare il blocco di memoria all'esterno di un ciclo e riutilizzarlo all'interno del ciclo.

Il contenuto della memoria appena allocata non è definito. È consigliabile inizializzarlo prima dell'uso. Ad esempio, è possibile usare il metodo Span<T>.Clear che imposta tutti gli elementi sul valore predefinito di tipo T.

È possibile usare la sintassi dell'inizializzatore di matrice per definire il contenuto della memoria appena allocata. Nell'esempio seguente vengono illustrati vari modi per eseguire questa operazione.

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];

Nell'espressione stackalloc T[E], T deve essere un tipo non gestito e E deve restituire un valore int non negativo. Quando si usa la sintassi dell'espressione di raccolta per inizializzare l'intervallo, il compilatore può usare l'archiviazione allocata dello stack per un intervallo se non viola la protezione dei riferimenti.

Sicurezza

L'uso di stackalloc attiva automaticamente le funzionalità di rilevazione del sovraccarico del buffer in Common Language Runtime (CLR). Se viene rilevato un sovraccarico del buffer, il processo viene terminato il più rapidamente possibile per ridurre al minimo la possibilità che venga eseguito codice dannoso.

Specifiche del linguaggio C#

Per altre informazioni, vedere la sezione Allocazione dello stack della specifica del linguaggio C# e la nota sulla proposta di funzionalità Consenti stackalloc nei contesti annidati.

Vedi anche