Twoja tablica jest alokowana na stercie, a wartości int nie są opakowane.
Przyczyną twojego zamieszania jest prawdopodobnie to, że ludzie powiedzieli, że typy odwołań są przydzielane na stercie, a typy wartości są przydzielane na stosie. To nie jest całkowicie dokładne przedstawienie.
Wszystkie lokalne zmienne i parametry są przydzielane na stosie. Obejmuje to zarówno typy wartości, jak i typy odwołań. Różnica między nimi polega tylko na tym, co jest przechowywane w zmiennej. Nic dziwnego, że w przypadku typu wartości wartość typu jest przechowywana bezpośrednio w zmiennej, a dla typu referencyjnego wartość typu jest przechowywana na stercie, a odwołanie do tej wartości jest przechowywane w zmiennej.
To samo dotyczy pól. Gdy pamięć jest przydzielana dla instancji typu zagregowanego (a class
lub a struct
), musi ona obejmować pamięć dla każdego z jej pól instancji. W przypadku pól typu referencyjnego ta pamięć przechowuje tylko odniesienie do wartości, która zostanie później przydzielona na stercie. W przypadku pól typu wartość ta pamięć przechowuje rzeczywistą wartość.
Tak więc, biorąc pod uwagę następujące typy:
class RefType{
public int I;
public string S;
public long L;
}
struct ValType{
public int I;
public string S;
public long L;
}
Wartości każdego z tych typów wymagałyby 16 bajtów pamięci (przy założeniu 32-bitowego rozmiaru słowa). W I
każdym przypadku pole zajmuje 4 bajty na przechowywanie swojej wartości, pole S
zajmuje 4 bajty na przechowywanie odniesienia, a pole L
zajmuje 8 bajtów na przechowywanie wartości. Więc pamięć dla wartości obu RefType
i ValType
wygląda tak:
0 ┌───────────────────┐
│ I │
4 ├───────────────────┤
│ S │
8 ├───────────────────┤
│ L │
│ │
16 └───────────────────┘
Teraz, jeśli miał trzy zmienne lokalne w funkcji, typów RefType
, ValType
oraz int[]
, jak to:
RefType refType;
ValType valType;
int[] intArray;
Twój stos może wyglądać następująco:
0 ┌───────────────────┐
│ refType │
4 ├───────────────────┤
│ valType │
│ │
│ │
│ │
20 ├───────────────────┤
│ intArray │
24 └───────────────────┘
Jeśli przypisałeś wartości do tych zmiennych lokalnych, na przykład:
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;
Wtedy twój stos może wyglądać mniej więcej tak:
0 ┌───────────────────┐
│ 0x4A963B68 │ - adres sterty „refType”
4 ├───────────────────┤
│ 200 │ - wartość „valType.I”
│ 0x4A984C10 │ - adres sterty „valType.S”
│ 0x44556677 │ - niskie 32 bity wartości „valType.L”
│ 0x00112233 │ - wysokie 32 bity wartości „valType.L”
20 ├───────────────────┤
│ 0x4AA4C288 │ - adres sterty „intArray”
24 └───────────────────┘
Pamięć pod adresem 0x4A963B68
(wartość refType
) wyglądałaby tak:
0 ┌───────────────────┐
│ 100 │ - wartość „refType.I”
4 ├───────────────────┤
│ 0x4A984D88 │ - adres sterty „refType.S”
8 ├───────────────────┤
│ 0x89ABCDEF │ - niskie 32 bity wartości „refType.L”
│ 0x01234567 │ - wysokie 32 bity wartości „refType.L”
16 └───────────────────┘
Pamięć pod adresem 0x4AA4C288
(wartość intArray
) wyglądałaby tak:
0 ┌───────────────────┐
│ 4 │ - długość tablicy
4 ├───────────────────┤
│ 300 │ - `intArray [0]`
8 ├───────────────────┤
│ 301 │ - `intArray [1]`
12 ├───────────────────┤
│ 302 │ - `intArray [2]`
16 ├───────────────────┤
│ 303 │ - `intArray [3]`
20 └───────────────────┘
Teraz, jeśli przekazałeś intArray
do innej funkcji, wartość umieszczona na stosie byłaby 0x4AA4C288
adresem tablicy, a nie kopią tablicy.