Массивы, куча, стек и типы значений

git reset HEAD~1, если вы не хотите, чтобы ваши изменения исчезли (нестатические изменения). Изменить, зафиксировать и снова нажать git push -f [origin] [branch]

127
задан svick 22 October 2011 в 00:27
поделиться

6 ответов

Ваш массив размещен в куче, а целые числа не упаковываются.

Источник вашего путаница вероятна из-за того, что люди сказали, что ссылочные типы выделяются в куче, а типы значений - в стеке. Это не совсем точное представление.

Все локальные переменные и параметры размещены в стеке. Это включает как типы значений, так и ссылочные типы. Разница между ними только в том, что хранится в переменной. Неудивительно, что для типа значения значение типа сохраняется непосредственно в переменной, а для ссылочного типа значение типа хранится в куче, а ссылка на это значение - это то, что хранится в переменной.

То же самое верно и для полей. Когда память выделяется для экземпляра агрегированного типа (класса или структуры), она должна включать хранилище для каждого из его полей экземпляра. Для полей ссылочного типа это хранилище содержит только ссылку на значение, которое позже будет выделено в куче. Для полей типа значения это хранилище содержит фактическое значение.

Итак, учитывая следующие типы:

class RefType{
    public int    I;
    public string S;
    public long   L;
}

struct ValType{
    public int    I;
    public string S;
    public long   L;
}

Для значений каждого из этих типов потребуется 16 байт памяти (при 32-битном размере слова). Поле I в каждом случае занимает 4 байта для хранения своего значения, поле S занимает 4 байта для хранения своей ссылки, а поле L занимает 8 байтов для хранения своего значения. Таким образом, память для значений как RefType , так и ValType выглядит следующим образом:

 0 ┌───────────────────┐
   │        I          │
 4 ├───────────────────┤
   │        S          │
 8 ├───────────────────┤
   │        L          │
   │                   │
16 └───────────────────┘

Теперь, если у вас есть три локальные переменные в функции типов RefType , ValType и int [] , например:

RefType refType;
ValType valType;
int[]   intArray;

, тогда ваш стек может выглядеть так:

 0 ┌───────────────────┐
   │     refType       │
 4 ├───────────────────┤
   │     valType       │
   │                   │
   │                   │
   │                   │
20 ├───────────────────┤
   │     intArray      │
24 └───────────────────┘

Если вы присвоили значения этим локальным переменным, например:

refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;

valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;

intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;

Тогда ваш стек может выглядеть примерно так:

 0 ┌───────────────────┐
   │    0x4A963B68     │ -- heap address of `refType`
 4 ├───────────────────┤
   │       200         │ -- value of `valType.I`
   │    0x4A984C10     │ -- heap address of `valType.S`
   │    0x44556677     │ -- low 32-bits of `valType.L`
   │    0x00112233     │ -- high 32-bits of `valType.L`
20 ├───────────────────┤
   │    0x4AA4C288     │ -- heap address of `intArray`
24 └───────────────────┘

Память по адресу 0x4A963B68 (значение refType ) будет примерно так:

 0 ┌───────────────────┐
   │       100         │ -- value of `refType.I`
 4 ├───────────────────┤
   │    0x4A984D88     │ -- heap address of `refType.S`
 8 ├───────────────────┤
   │    0x89ABCDEF     │ -- low 32-bits of `refType.L`
   │    0x01234567     │ -- high 32-bits of `refType.L`
16 └───────────────────┘

Память по адресу 0x4AA4C288 (значение intArray ]) будет выглядеть примерно так:

 0 ┌───────────────────┐
   │        4          │ -- length of array
 4 ├───────────────────┤
   │       300         │ -- `intArray[0]`
 8 ├───────────────────┤
   │       301         │ -- `intArray[1]`
12 ├───────────────────┤
   │       302         │ -- `intArray[2]`
16 ├───────────────────┤
   │       303         │ -- `intArray[3]`
20 └───────────────────┘

Теперь, если вы передадите intArray другой функции,

275
ответ дан 24 November 2019 в 00:29
поделиться

Да, массив будет расположен в куче.

Целые числа внутри массива не будут упакованы. Тот факт, что тип значения существует в куче, не обязательно означает, что он будет упакован. Упаковка будет происходить только тогда, когда тип значения, такой как int, назначен ссылке на объект типа.

Например

Не упаковывает:

int i = 42;
myIntegers[0] = 42;

Ящики:

object i = 42;
object[] arr = new object[10];  // no boxing here 
arr[0] = 42;

Вы также можете проверить сообщение Эрика на эту тему:

23
ответ дан 24 November 2019 в 00:29
поделиться

Чтобы понять, что происходит, вот некоторые факты:

  • Объект всегда размещается в куче.
  • Куча содержит только объекты.
  • Типы значений выделяются в куче. стек или часть объекта в куче.
  • Массив - это объект.
  • Массив может содержать только типы значений.
  • Ссылка на объект - это тип значения.

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

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

20
ответ дан 24 November 2019 в 00:29
поделиться

В куче размещен массив целых чисел, ни больше, ни меньше. myIntegers - ссылки на начало раздела, в котором размещаются целые числа. Эта ссылка находится в стеке.

Если у вас есть массив объектов ссылочного типа, например, типа Object, myObjects [], расположенный в стеке, будет ссылаться на набор значений, которые ссылаются на сами объекты.

] Подводя итог, если вы передаете myIntegers некоторым функциям, вы передаете ссылку только на то место, где размещена реальная группа целых чисел.

1
ответ дан 24 November 2019 в 00:29
поделиться

Я думаю, что в основе вашего вопроса лежит непонимание ссылочных типов и типов значений. Это то, с чем, вероятно, боролся каждый разработчик .NET и Java.

Массив - это просто список значений. Если это массив ссылочного типа (скажем, строка [] ), то массив представляет собой список ссылок на различные строковые объекты в куче, поскольку ссылка - ] значение ссылочного типа. Внутри эти ссылки реализованы как указатели на адрес в памяти. Если вы хотите это визуализировать, такой массив в памяти (в куче) будет выглядеть так:

[00000000, 00000000, 00000000, F8AB56AA]

Это массив строки , который содержит 4 ссылки на строковые объекты в куче (цифры здесь являются шестнадцатеричными). В настоящее время только последняя строка на самом деле указывает на что-либо (при выделении память инициализируется всеми нулями), этот массив будет в основном результатом этого кода в C #:

string[] strings = new string[4];
strings[3] = "something"; // the string was allocated at 0xF8AB56AA by the CLR

Вышеупомянутый массив будет в 32-битная программа. В 64-битной программе ссылки будут вдвое больше ( F8AB56AA будет 00000000F8AB56AA ).

Если у вас есть массив типов значений (скажем, int [] ), то массив представляет собой список целых чисел, так как значение типа значения равно само значение (отсюда и название).

[00000000, 45FF32BB, 00000000, 00000000]

Это массив из 4 целых чисел, где только второму int присваивается значение (до 1174352571, которое является десятичным представлением этого шестнадцатеричного числа) и остальные целые числа будут равны 0 (как я уже сказал, память инициализируется нулем, а 00000000 в шестнадцатеричном формате равняется 0 в десятичном). Код, создавший этот массив, будет выглядеть так:

 int[] integers = new int[4];
 integers[1] = 1174352571; // integers[1] = 0x45FF32BB would be valid too

Этот int [] массив также будет храниться в куче.

В качестве другого примера, память short [4] массив будет выглядеть так:

[0000, 0000, 0000, 0000]

Поскольку значение короткого короткого представляет собой 2-байтовое число.

Где хранится тип значения, это просто деталь реализации, как очень хорошо объясняет Эрик Липперт здесь , не являющаяся неотъемлемой частью различий между типами значений и ссылочных типов (что является различием в поведении).

Когда вы передаете что-то методу (будь то ссылочный тип или тип значения), то копия значения типа фактически передается методу. В случае ссылочного типа значение является ссылкой (воспринимайте это как указатель на часть памяти, хотя это также является деталью реализации), а в случае типа значения - ценность - это сама вещь.

// Calling this method creates a copy of the *reference* to the string
// and a copy of the int itself, so copies of the *values*
void SomeMethod(string s, int i){}

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

object o = 5;
9
ответ дан 24 November 2019 в 00:29
поделиться

В вашем примере кода нет боксов.

Типы значений могут находиться в куче, как и в вашем массиве целых чисел. Массив размещается в куче и хранит целые числа, которые являются типами значений. Содержимое массива инициализируется значением по умолчанию (int), которое оказывается равным нулю.

Рассмотрим класс, который содержит тип значения:


    class HasAnInt
    {
        int i;
    }

    HasAnInt h = new HasAnInt();

Переменная h относится к экземпляру HasAnInt, который находится в куче. Просто он содержит тип значения. Это совершенно нормально, просто «i» живет в куче, поскольку содержится в классе. В этом примере тоже нет бокса.

1
ответ дан 24 November 2019 в 00:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: