Когда происходит выделение памяти под переменную?

если я объявил переменную но не присвоил ей значение, например int i; память в стеке под нее выделяется сразу или только когда ей присвоится какое-либо значение?


Ответы (2 шт):

Автор решения: A K

В ваших терминах - "сразу". И будет ей присвоено значение по умолчанию для типа int - то есть ноль. (А например для классов таким значением по умолчанию является null)

Вы вполне могли бы провести натурный эксперимент, накидав простой код типа такого:

введите сюда описание изображения

Очень полезно писать такие простенькие скетчи - это даёт вам возможность лучше понимать, как устроена система, придумывать свои собственные простые кейсы для проверки и научиться писать код из головы.

→ Ссылка
Автор решения: Alexander Petrov

На такие простые вопросы всегда невероятно трудно ответить. Потому что простого и короткого ответа нет.

Начнём с того, что компиляция кода 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 - предлагаю ознакомиться с этой статьёй.

→ Ссылка