Почему stackalloc должен использоваться как инициализатор переменной?

Я пишу небезопасный код на 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;
}

Почему не допускается первая версия, и какие злые вещи произойдут, если я попробую вторую версию?

1 ответ

Решение

Ваш стек выглядит примерно так:

[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Все буферы просто игнорируются (что является одним из преимуществ 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 может перезаписать их случайными байтами в случайных местах.

И это только с учетом единственного потока, задействованного здесь, не говоря уже о том, что другие потоки с отдельными стеками, возможно, смогут получить доступ к этому полю.

Это также просто не очень полезно. Вы можете заставить поле удерживать адрес где-то в стеке с достаточным усилием, но мало что можно с этим сделать.

Другие вопросы по тегам