Mảng của bạn được phân bổ trên heap và ints không được đóng hộp.
Nguồn gốc của sự nhầm lẫn của bạn có thể là do mọi người đã nói rằng các loại tham chiếu được phân bổ trên heap và các loại giá trị được phân bổ trên ngăn xếp. Đây không phải là một đại diện hoàn toàn chính xác.
Tất cả các biến và tham số cục bộ được phân bổ trên ngăn xếp. Điều này bao gồm cả loại giá trị và loại tham chiếu. Sự khác biệt giữa hai chỉ là những gì được lưu trữ trong biến. Không có gì đáng ngạc nhiên, đối với một loại giá trị, giá trị của loại được lưu trữ trực tiếp trong biến và đối với loại tham chiếu, giá trị của loại được lưu trữ trên heap và tham chiếu đến giá trị này là giá trị được lưu trữ trong biến.
Điều tương tự giữ cho các lĩnh vực. Khi bộ nhớ được cấp phát cho một thể hiện của loại tổng hợp (a class
hoặc a struct
), nó phải bao gồm lưu trữ cho mỗi trường đối tượng của nó. Đối với các trường loại tham chiếu, bộ lưu trữ này chỉ giữ một tham chiếu đến giá trị, chính nó sẽ được phân bổ trên heap sau này. Đối với các trường loại giá trị, bộ lưu trữ này giữ giá trị thực.
Vì vậy, đưa ra các loại sau:
class RefType{
public int I;
public string S;
public long L;
}
struct ValType{
public int I;
public string S;
public long L;
}
Các giá trị của mỗi loại này sẽ cần 16 byte bộ nhớ (giả sử kích thước từ 32 bit). Trường I
trong mỗi trường hợp mất 4 byte để lưu trữ giá trị của nó, trường S
mất 4 byte để lưu trữ tham chiếu của nó và trường L
mất 8 byte để lưu trữ giá trị của nó. Vì vậy, bộ nhớ cho giá trị của cả hai RefType
và ValType
trông như thế này:
0 ┌───────────────────
Tôi
4 ├───────────────────
│ S
8 ├───────────────────
│ L
│ │
16 └───────────────────
Bây giờ nếu bạn có ba biến cục bộ trong một hàm, các loại RefType
, ValType
và int[]
, như thế này:
RefType refType;
ValType valType;
int[] intArray;
sau đó ngăn xếp của bạn có thể trông như thế này:
0 ┌───────────────────
│ refType
4 ├───────────────────
│ valType │
│ │
│ │
│ │
20 ├───────────────────
IntArray
24 ───────────────────
Nếu bạn đã gán giá trị cho các biến cục bộ này, như vậy:
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;
Sau đó, ngăn xếp của bạn có thể trông giống như thế này:
0 ┌───────────────────
0x4A963B68 - địa chỉ heap của `refType`
4 ├───────────────────
200 │ - giá trị của `valType.I`
│ 0x4A984C10 - địa chỉ heap của `valType.S`
0x44556677 - 32 bit thấp của `valType.L`
0x00112233 - 32 bit cao của `valType.L`
20 ├───────────────────
0x4AA4C288 - địa chỉ heap của `intArray`
24 ───────────────────
Bộ nhớ tại địa chỉ 0x4A963B68
(giá trị của refType
) sẽ giống như:
0 ┌───────────────────
│ 100 │ - giá trị của `refType.I`
4 ├───────────────────
0x4A984D88 - địa chỉ heap của `refType.S`
8 ├───────────────────
│ 0x89ABCDEF - 32 bit của `refType.L`
0x01234567 - cao 32 bit của `refType.L`
16 └───────────────────
Bộ nhớ tại địa chỉ 0x4AA4C288
(giá trị của intArray
) sẽ giống như:
0 ┌───────────────────
4 │ - chiều dài của mảng
4 ├───────────────────
│ 300 │ - `intArray [0]`
8 ├───────────────────
│ 301 │ - `intArray [1]`
12 ├───────────────────
│ 302 │ - `intArray [2]`
16 ├───────────────────
│ 303 │ - `intArray [3]`
20 └───────────────────
Bây giờ, nếu bạn chuyển intArray
đến một hàm khác, giá trị được đẩy lên ngăn xếp sẽ là 0x4AA4C288
địa chỉ của mảng chứ không phải là bản sao của mảng.