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> 变量时,不必使用 unsafe 上下文。

    使用这些类型时,可以在条件表达式或赋值表达式中使用 stackalloc 表达式,如以下示例所示:

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

    只要允许使用 Span<T>ReadOnlySpan<T> 变量,就可以在其他表达式中使用 stackalloc 表达式或集合表达式,如下例所示:

    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 值。 使用集合表达式语法来初始化范围时,如果编译器没有违反 ref 安全性,编译器可能会对范围使用堆栈分配的存储。

安全性

使用 stackalloc 会自动启用公共语言运行时 (CLR) 中的缓冲区溢出检测功能。 如果检测到缓冲区溢出,则将尽快终止进程,以便将执行恶意代码的可能性降到最低。

C# 语言规范

有关详细信息,请参阅 C# 语言规范以及允许嵌入上下文中的 stackalloc 功能建议说明的堆栈分配部分。

另请参阅