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
功能建议说明的堆栈分配部分。