Почему 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
То, что вы предлагаете, это то, что частное поле, то есть часть объекта в куче (другой фрагмент памяти, управляемый по-разному), содержит указатель на буфер, который был наполовину перезаписан совершенно разными данными, разных типов.
Сразу же последствия будут:
- Попытки использовать
buffer
теперь чреваты, потому что половина из них перезаписывается элементом, большинство из которых даже не байты. - Попытки использовать любой местный язык теперь чреваты, потому что будущие изменения
buffer
может перезаписать их случайными байтами в случайных местах.
И это только с учетом единственного потока, задействованного здесь, не говоря уже о том, что другие потоки с отдельными стеками, возможно, смогут получить доступ к этому полю.
Это также просто не очень полезно. Вы можете заставить поле удерживать адрес где-то в стеке с достаточным усилием, но мало что можно с этим сделать.