为什么stackalloc必须用作变量初始化项?

本文关键字:变量 初始化 stackalloc 为什么 | 更新日期: 2023-09-27 18:10:43

我在c#中编写一些不安全的代码(后续这个问题),我想知道,为什么stackalloc关键字必须用作变量初始化器?这将产生一个语法错误:

public unsafe class UnsafeStream
{
    byte* buffer;
    public UnsafeStream(int capacity)
    {
        this.buffer = stackalloc byte[capacity]; // "Invalid expression term 'stackalloc' / ; expected / } expected"
    }
}

但是重新赋值本地临时变量的结果不会:

public UnsafeStream(int capacity)
{
    byte* buffer = stackalloc byte[capacity];
    this.buffer = buffer;
}

为什么第一个版本不允许,如果我尝试第二个版本会发生什么坏事?

为什么stackalloc必须用作变量初始化项?

您的堆栈看起来大致是这样的:

[stuff from earlier calls][stuff about where this came from][this][capacity]
                                                                   ^You are here

然后执行stackalloc,这将在堆栈中添加两个东西,指针和指向的数组:

[stuff from earlier calls][stuff about where this came from][this][capacity][buffer][array pointed to by buffer]
                                                                                            ^You are here

然后当你返回最近放在堆栈上的东西时,当前函数的局部变量,它的返回地址和stackalloc ed缓冲区都被简单地忽略(这是stackalloc的优点之一,忽略东西是快速和容易的):

[stuff from earlier calls][stuff about where this came from][this][capacity][buffer][array pointed to by buffer]
                       ^You are here

它可以被下一个方法调用覆盖:

[stuff from earlier calls][stuff about where this came from][this][new local1][new local2]o by buffer]
                                                                                 ^You are here

你所建议的,是一个私有字段,也就是说,堆上对象的一部分(不同的内存,以不同的方式管理)持有一个指向缓冲区的指针,该指针已被完全不同类型的不同数据覆盖了一半。

直接后果是:

  1. 尝试使用buffer现在充满了,因为它的一半被item覆盖,其中大多数甚至不是字节。
  2. 尝试使用任何本地现在充满了,因为未来对buffer的更改可以在随机位置用随机字节覆盖它们。

这只是考虑到这里涉及的单个线程,更不用说其他具有单独堆栈的线程可能能够访问该字段。

它也不是很有用。你可以通过足够的努力强制一个字段保存一个地址到堆栈的某个地方,但是没有多少好的方法可以使用它。