Когда происходит выделение памяти под переменную?
если я объявил переменную но не присвоил ей значение, например int i; память в стеке под нее выделяется сразу или только когда ей присвоится какое-либо значение?
Ответы (2 шт):
В ваших терминах - "сразу". И будет ей присвоено значение по умолчанию для типа int - то есть ноль. (А например для классов таким значением по умолчанию является null)
Вы вполне могли бы провести натурный эксперимент, накидав простой код типа такого:
Очень полезно писать такие простенькие скетчи - это даёт вам возможность лучше понимать, как устроена система, придумывать свои собственные простые кейсы для проверки и научиться писать код из головы.
На такие простые вопросы всегда невероятно трудно ответить. Потому что простого и короткого ответа нет.
Начнём с того, что компиляция кода c# проходит несколько стадий: сперва в IL-код, потом в машинный код. И память под переменную где-то там выделяется в обоих случаях.
Возможно, говоря "переменная", вы имели в виду вообще любую программную сущность: это может быть и поле класса, и параметр метода.
Рассмотрим такой код:
class Test
{
int instanceField;
static int staticField;
public void Foo(int param)
{
int variableA = 42;
Console.WriteLine(variableA);
}
public void Bar()
{
int variableB = 42;
var range = Enumerable.Range(0, 10).Where(x => x == variableB);
Console.WriteLine(range.Count());
}
}
Память под поле instanceField будет выделена при создании экземпляра класса. Скорей всего она будет в куче, но в последниях версиях .NET появился Escape analysis и память под объект в некоторых случаях может быть выделена на стеке.
Память под статическое поле staticField будет выделена, если я не ошибаюсь, при загрузке сборки.
Память под параметр param будет выделена при вызове метода. В зависимости от соглашений о вызове (Calling convention), рантайма (CLR, Mono) и уровня оптимизаций (Debug, Release) она может быть в стеке, в регистре, или бог знает где ещё.
Память под локальную переменную variableA будет выделена, скорее всего, в момент компиляции (JIT или AOT) метода Foo. На стеке или в регистре. Но если, например, убрать или закомментировать вызов Console.WriteLine(variableA);, то компилятор может выкинуть эту неиспользуемую переменную, в итоге память под неё вообще никогда не будет выделяться.
А ещё в последних версиях дотнета есть Tiered compilation. В ходе неё сперва может быть быстро выдан слабооптимизированный код (память под переменную выделена в одном месте), потом код может быть перекомпилирован в сильнооптимизированный (память выделена в другом месте).
С переменной variableB всё ещё интереснее! Она участвует в замыкании. В результате компилятором будет создан класс, содержащий эту переменную в качестве поля. Соответственно, память будет выделена в момент создания экземпляра этого класса.
В общем, всё сложно.
The Truth About Value Types - предлагаю ознакомиться с этой статьёй.
