#c_sharp #массивы
Узнал что массивы в C#-это типы ссылок и они хранятся в куче,которая используется для динамичного выделения памяти.Но вот вопрос:для чего массиву динамичное выдиление памяти?.Вот например мы объявляем массив int[] newArray = new [4]; Мы заранее знаем что массив будет принимать 4 елемнта типа Int32 и мы заранее знаем сколько памяти ему потребуется.Так почему массивы-типы ссылок?
Ответы
Ответ 1
"Сколько памяти потребуется" важно не столько при выделении памяти, сколько при хранении массива в других объектах. Например, у вас есть класс вида class A { public int fieldA; public int fieldB; } И есть работающий с ним код вида var a = new A(); a.fieldB = 42; Перед реальным выполнением этот код надо превратить в код для конкретной платформы. JIT компилятор, который этим занимается, заранее знает, сколько объект типа A занимает в памяти, и по какому смещению в нем лежит поле fieldB: [0 header][+24 fieldA][+28 fieldB] // 24 - условный размер заголовка и он генеренирует что-то вроде mov [a + 28], 42 // положить по адресу a + смещение в нем поля fieldB значение 42 И все это работает быстро. Теперь добавим поле-массив (в котором может лежать массив, а может и не лежать): class A { public int fieldA; public int[] fieldArray; public int fieldB; } Получаем объект вида [0 header][+24 fieldA][+28 fieldArray][+32 fieldB] // размер fieldArray предсказуем, т.к. массив - ссылочный тип, и в этом поле лежит адрес массива. и код при выполнении mov [a + 32], 42 // положить по адресу a + смещение в нем поля fieldB значение 42 Ок, теперь представим что int[] - это value type. [0 header][+24 fieldA][+28 fieldArray][+??? fieldB] // размер fieldArray заранее неизвестен Как теперь в этой ситуации записать значение в fieldB? Реально проверять размер массива перед каждым обращением в соседнее поле? Перекомпилировать метод из IL в C# заново под каждый объект? Дорого, сложно и неэффективно. Разрешить массивы (и, соответственно, строчки) только заранее известной длины? Ну тогда на языке вообще будет тяжело что-то реальное написать :) А вот если убрать необходимость как-то хранить массивы в других объектах - то все ок, можно делать из них value type. Именно так сделано для структуры Span, которая, по сути, и есть value type array. Вот, выделяет 100 байт чистейшего стека, без всяких там ссылок: Span stackSpan = stackalloc byte[100]; Ответ 2
На вопрос "Почему?" краткий ответ таков: потому что на данный момент так сделано. Можно ли сделать иначе: если массив умещается на стеке - размещать его там, не умещается - размещать в куче? Да, можно. В JVM делается именно так (во всяком случае, в некоторых реализациях HotSpot). Почему не сделано в CLR? Не всё сразу. Разработчики .NET предпочли реализовывать другие фичи (значимые типы, дженерики, указатели на неуправляемую память и пр.) Однако, работы по определению, где выгоднее разместить объекты, ведутся. Object Stack Allocation. Надеюсь, эти теоретические работы перейдут в практическую плоскость.Ответ 3
Потому что можно сделать так: int[] i; i = new int[15]; //тут какое либо использование i = new int[25]; //ещё что либо n = int.Parse(Console.ReadLine()/*Любой другой способ получения информации от пользователя*/) i = new int[n]; //ещё что либо А если быть более честым, то c# полностью объектно-ориентированный, и всё что в нём есть - объект, а объект - это составная сущность которая занимает (зачастую) неопределённое количество памяти. Мы заранее знаем что массив будет принимать 4 елемнта типа Int32 и мы заранее знаем сколько памяти ему потребуется. Да и тут я бы с вами поспорил. А как же вычисление значений не на этапе компиляции, а в рантайме?(во время выполнения) Простой пример, который я часто использую: мне бывает лень объявлять масивчики и я пользуюсь таким методом(читай: отрывок паттерна Фабрика. хоть вообще не красивый, не правильный и нельзя так делать, но блин удобно) GetArr(int lenght)=> new T[lenght]; Это универсальный метод для создания Массива любого типа. А ещё, небольшая разница между тем же самым Int32. Это не класс а структура, а структуры являются TypeValue - значениями, и соответственно хранятся в стеке, а Array является производным от Object, и соответственно располагается в Куче
Комментариев нет:
Отправить комментарий