Как хранится массив структур?

Как хранятся данные массив структур и классов. То есть, массив это ссылочный тип, а структура значимый, то есть хранится в стеке. И еще массивы структур в бенчмарке занимают место в куче, хотя не должны, ведь они должны храниться в стеке. Куда ссылаются ссылки массива? И тем же макаром объясните массив классов.


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

Автор решения: aepot

Вы просто запутались, распутаться легко.

Значимый тип - это кусок памяти с данными, например int это 4 байта, представляющие число. int это структура. Для массива интов CLR выделяет N * 4 байт памяти.

Ссылочный тип состоит из 2 частей: ссылка и сам объект. Ссылка - это тот же по сути int, те же правила, значимый тип. Разница только в том что тип IntPtr, который используется для ссылок в 32-битных приложениях имеет размер 4 байта, а в 64-битных - 8 байт.

То есть грубо говоря массив любых ссылочных объектов object[] это мехнически ни что иное, как IntPtr[]. Другими словами, массив хранит только ссылки и тип для этих ссылок в своих метаданных. Сам же тип данных в массиве никак не влияет на его поведение. Любой [] массив хранит данные только в куче.

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

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

Вопрос только в том, будет память выделена заранее в стеке сразу или она будет выделена по необходимости еще одним дополнительным обращением к системе за ресурсами.

Смотрите на исходный тип самой переменной. Если он значимый - значит память заранее выделится и будет лежать в стеке как значение. Если он ссылочный, то память выделится ровно под ссылку, заполнять вы её будете отдельным обращением за ресурсами.

Переменная для массива ссылочная - значит память выделится под ссылку, сам массив отдельным запросом. Создали массив - какие в нём переменные будут? Если значимые, значит массив со значениями. Если ссылочные - значит массив ссылок и всё остальное опять же отдельным запросом к системе.

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

А дальше вы уже можете играть в матрешку, создать объект в котором есть структура, у которой есть ссылка на объект, у которой есть структура и т.д. Опять же единственный вопрос, будет ли память выделена сразу под данные или только под ссылку (а данные уже отдельно запрашиваются по необходимости).

struct StackMemory 
{
    public HeapMemory heap;
}

class HeapMemory 
{
    public StackMemory stack;
}

class Program
{
    StackMemory stack;
    
    private void Run() 
    {
        stack.heap = new HeapMemory();
        stack.heap.stack.heap = new HeapMemory();
        stack.heap.stack.heap.stack.heap = new HeapMemory();
    }

    static void Main(string[] args) => new Program().Run();
}

Запросили new HeapMemory() - место под StackMemory выделилось сразу с запросом в котором только место под ссылку на следующую HeapMemory. И так по кругу. Еще учитывайте, что это примерное поведение, на самом деле программа не обязана вам сразу выделять память в стеке и спокойно может хранить его где-то в куче.

→ Ссылка