3

我正在用 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;
}

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

4

1 回答 1

4

您的堆栈看起来非常大致如下:

[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

然后,当您返回最近放入堆栈的内容时,当前函数的局部变量、其返回地址和stackalloced 缓冲区都将被简单地忽略(这是 的优点之一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令人担忧,因为其中一半被项目覆盖,其中大部分甚至不是字节。
  2. 尝试使用任何本地现在令人担忧,因为未来的更改buffer可能会在随机位置用随机字节覆盖它们。

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

它也不是很有用。您可以通过足够的努力强制一个字段将地址保存到堆栈中的某个位置,但是对此没有什么好处。

于 2015-09-02T01:49:47.290 回答