L'array è allocato sull'heap e gli ints non sono inscatolati.
La fonte della tua confusione è probabilmente perché le persone hanno detto che i tipi di riferimento sono allocati sullo heap e che i tipi di valore sono allocati sullo stack. Questa non è una rappresentazione del tutto accurata.
Tutte le variabili e i parametri locali sono allocati nello stack. Ciò include sia tipi di valore che tipi di riferimento. La differenza tra i due è solo ciò che è memorizzato nella variabile. Non sorprende che, per un tipo di valore, il valore del tipo sia memorizzato direttamente nella variabile e, per un tipo di riferimento, il valore del tipo è memorizzato nell'heap e un riferimento a questo valore è ciò che è memorizzato nella variabile.
Lo stesso vale per i campi. Quando la memoria viene allocata per un'istanza di un tipo aggregato (a class
o a struct
), deve includere l'archiviazione per ciascuno dei suoi campi di istanza. Per i campi del tipo di riferimento, questa memoria contiene solo un riferimento al valore, che verrà allocato sull'heap in un secondo momento. Per i campi di tipo valore, questa memoria contiene il valore effettivo.
Quindi, dati i seguenti tipi:
class RefType{
public int I;
public string S;
public long L;
}
struct ValType{
public int I;
public string S;
public long L;
}
I valori di ciascuno di questi tipi richiederebbero 16 byte di memoria (assumendo una dimensione di parola a 32 bit). Il campo I
richiede in ogni caso 4 byte per memorizzare il suo valore, il campo S
richiede 4 byte per memorizzare il suo riferimento e il campo L
impiega 8 byte per memorizzare il suo valore. Quindi la memoria per il valore di entrambi RefType
e ValType
assomiglia a questo:
0 ┌───────────────────┐
│ I │
4 ├───────────────────┤┤
│ S │
8 ├───────────────────┤┤
│ L │
│ │
16 └───────────────────┘┘
Ora, se tu avessi tre variabili locali in una funzione, di tipi RefType
, ValType
e int[]
, in questo modo:
RefType refType;
ValType valType;
int[] intArray;
quindi il tuo stack potrebbe apparire così:
0 ┌───────────────────┐
│ refType │
4 ├───────────────────┤┤
│ valType │
│ │
│ │
│ │
20 ├───────────────────┤
│ intArray │
24 └───────────────────┘
Se hai assegnato valori a queste variabili locali, in questo modo:
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;
Quindi il tuo stack potrebbe assomigliare a questo:
0 ┌───────────────────┐
│ 0x4A963B68 │ - indirizzo heap di `refType`
4 ├───────────────────┤┤
│ 200 │ - valore di `valType.I`
│ 0x4A984C10 │ - indirizzo heap di `valType.S`
│ 0x44556677 │ - basso 32 bit di `valType.L`
│ 0x00112233 │ - 32 bit alti di `valType.L`
20 ├───────────────────┤
│ 0x4AA4C288 │ - indirizzo heap di `intArray`
24 └───────────────────┘
La memoria all'indirizzo 0x4A963B68
(valore di refType
) sarebbe simile a:
0 ┌───────────────────┐
│ 100 │ - valore di `refType.I`
4 ├───────────────────┤┤
│ 0x4A984D88 │ - indirizzo heap di `refType.S`
8 ├───────────────────┤┤
│ 0x89ABCDEF │ - basso 32 bit di `refType.L`
│ 0x01234567 │ - 32 bit alti di `refType.L`
16 └───────────────────┘┘
La memoria all'indirizzo 0x4AA4C288
(valore di intArray
) sarebbe simile a:
0 ┌───────────────────┐
│ 4 │ - lunghezza dell'array
4 ├───────────────────┤┤
│ 300 │ - `intArray [0]`
8 ├───────────────────┤┤
│ 301 │ - `intArray [1]`
12 ├───────────────────┤┤
│ 302 │ - `intArray [2]`
16 ├───────────────────┤┤
│ 303 │ - `intArray [3]`
20 └───────────────────┘
Ora, se si passasse intArray
a un'altra funzione, il valore inserito nello stack sarebbe 0x4AA4C288
, l'indirizzo dell'array, non una copia dell'array.