Mô tả về mảng
Mô tả mảng kế thừa một số tính năng của mô tả biến. Để bắt đầu, chúng ta nên lưu ý rằng mảng có thể là toàn cục và cục bộ, dựa trên vị trí khai báo của chúng. Tương tự như biến, các trình chỉnh sửa const
và static
cũng có thể được sử dụng để mô tả một mảng. Đối với mảng một chiều có kích thước cố định, cú pháp khai báo xuất hiện như sau:
type static1D[size];
Tại đây, type
và static1D
lần lượt biểu thị tên kiểu của các phần tử và mã định danh mảng, trong khi size
trong dấu ngoặc vuông là hằng số nguyên xác định kích thước.
Đối với mảng đa chiều, phải chỉ định nhiều kích cỡ khác nhau, tùy theo số lượng chiều:
type static2D[size1][size2];
type static3D[size1][size2][size3];
type static4D[size1][size2][size3][size4];
2
3
Mảng động được mô tả theo cách tương tự, ngoại trừ việc bỏ qua phần tử trong dấu ngoặc vuông đầu tiên (trước khi sử dụng mảng như vậy, phải phân bổ dung lượng bộ nhớ cần thiết cho mảng đó bằng hàm ArrayResize
, xem phần nói về mảng động).
type dynamic1D[];
type dynamic2D[][size2];
type dynamic3D[][size2][size3];
type dynamic4D[][size2][size3][size4];
2
3
4
Đối với mảng có kích thước cố định, việc khởi tạo được phép: Các giá trị khởi tạo được chỉ định cho các phần tử sau dấu bằng, dưới dạng danh sách được phân tách bằng dấu phẩy, toàn bộ danh sách được đặt trong dấu ngoặc nhọn. Ví dụ:
int array1D[3] = {10, 20, 30};
Ở đây, một mảng số nguyên có 3 phần tử sẽ lấy các giá trị 10
, 20
và 30
.
Với danh sách khởi tạo, không cần phải chỉ định kích thước mảng trong dấu ngoặc vuông (cho chiều thứ nhất). Trình biên dịch sẽ tự động đánh giá kích thước theo độ dài danh sách. Ví dụ:
int array1D[] = {10, 20, 30};
Giá trị ban đầu có thể là cả hằng số và biểu thức hằng số, tức là các công thức mà trình biên dịch có thể tính toán trong quá trình biên dịch.
Ví dụ, mảng sau được điền bằng số giây trong một phút, giờ, ngày và tuần (biểu diễn dưới dạng công thức minh họa rõ hơn 86400
hoặc 604800
):
int seconds[] = {60, 60 * 60, 60 * 60 * 24, 60 * 60 * 24 * 7};
Các giá trị như vậy thường được thiết kế như một macro tiền xử lý ở đầu mã, sau đó tên của macro này được chèn vào mọi nơi cần thiết trong văn bản. Tùy chọn này được mô tả trong phần liên quan đến Preprocessor .
Số lượng phần tử khởi tạo không được vượt quá kích thước mảng. Nếu không, trình biên dịch sẽ đưa ra thông báo lỗi "too many initializers-quá nhiều phần tử khởi tạo". Nếu số lượng giá trị nhỏ hơn kích thước mảng, các phần tử còn lại sẽ được khởi tạo bằng số không. Do đó, có một ký hiệu ngắn gọn để khởi tạo toàn bộ mảng bằng số không:
int array2D[2][3] = {0};
Hoặc chỉ cần dấu ngoặc rỗng:
int array2D[2][3] = {};
Nó hoạt động bất kể số lượng chiều.
Để khởi tạo mảng đa chiều, các danh sách phải được lồng nhau. Ví dụ:
int array2D[3][2] = {{1, 2}, {3, 4}, {5, 6}};
Ở đây, kích thước chiều thứ nhất của mảng là 3; do đó, hai dấu phẩy đóng khung 3 phần tử bên trong dấu ngoặc nhọn ngoài. Tuy nhiên, vì mảng là hai chiều, mỗi phần tử của nó là một mảng, đến lượt nó, kích thước của mỗi phần tử là 2. Đây là lý do tại sao mỗi phần tử biểu diễn một danh sách trong dấu ngoặc nhọn, mỗi danh sách chứa 2 giá trị.
Giả sử chúng ta cần một mảng chuyển vị (kích thước đầu tiên là 2 và kích thước thứ hai là 3), thì giá trị khởi tạo của nó sẽ thay đổi:
int array2D[2][3] = {{1, 3, 5}, {2, 4, 6}};
Chúng ta có thể bỏ qua một hoặc nhiều giá trị trong danh sách khởi tạo, nếu cần, bằng cách đánh dấu vị trí của chúng bằng dấu phẩy. Tất cả các phần tử bị bỏ qua cũng sẽ được khởi tạo bằng số không.
int array1D[3] = {, , 30};
Ở đây, phần tử đầu tiên sẽ bằng 0.
Cú pháp ngôn ngữ cho phép đặt dấu phẩy sau phần tử cuối cùng:
string messages[] =
{
"undefined",
"success",
"error",
};
2
3
4
5
6
Điều này đơn giản hóa việc thêm các phần tử mới, đặc biệt là đối với các mục nhập nhiều chuỗi. Đặc biệt, nếu chúng ta quên nhập dấu phẩy trước phần tử mới được thêm vào trong một mảng chuỗi, thì các chuỗi cũ và mới sẽ được hợp nhất trong một phần tử (có cùng chỉ mục), trong khi không có phần tử mới nào xuất hiện. Hơn nữa, một số mảng có thể được tạo tự động (bởi một chương trình khác hoặc bởi các macro). Do đó, sự xuất hiện thống nhất của tất cả các phần tử là điều tự nhiên.
"Heap" và "Stack"
Với các mảng có khả năng lớn, điều quan trọng là phải phân biệt giữa vị trí toàn cục và cục bộ trong bộ nhớ. Bộ nhớ cho các biến toàn cục và mảng được phân bổ trong heap
, tức là bộ nhớ trống có sẵn cho chương trình. Bộ nhớ này thực tế không bị giới hạn bởi bất cứ thứ gì, ngoại trừ các đặc điểm vật lý của máy tính và hệ điều hành của bạn.
Tên heap
được giải thích bởi thực tế là các vùng bộ nhớ có kích thước khác nhau luôn được chương trình phân bổ hoặc hủy phân bổ, dẫn đến các vùng trống được phân tán ngẫu nhiên trong toàn bộ khối. Các biến và mảng cục bộ nằm trong ngăn xếp, tức là một vùng bộ nhớ giới hạn được phân bổ trước cho chương trình, đặc biệt là cho các phần tử cục bộ.
Tên stack
bắt nguồn từ thực tế là trong quá trình thực thi thuật toán, các lệnh gọi lồng nhau của các hàm diễn ra, tích lũy dữ liệu bên trong theo nguyên tắc "tích lũy":
Ví dụ, OnStart
được gọi bởi thiết bị đầu cuối, một hàm từ mã bạn áp dụng được gọi từ OnStart
, sau đó hàm khác của bạn được gọi từ hàm trước đó, v.v. Đồng thời, khi nhập từng hàm, các biến cục bộ của hàm đó được tạo ra và tiếp tục ở đó khi hàm lồng nhau được gọi. Nó cũng tạo ra các biến cục bộ, được đưa vào stack nhiều hơn một chút so với các biến trước đó.
Do đó, stack
thường chứa một số lớp dữ liệu cục bộ từ tất cả các hàm đã được kích hoạt trên đường dẫn đến chuỗi mã hiện tại. Cho đến khi hàm ở trên cùng của stack hoàn tất, dữ liệu cục bộ của nó sẽ bị xóa khỏi đó. Nhìn chung, stack là một bộ lưu trữ hoạt động theo nguyên tắc FILO/LIFO (First In Last Out, Last In First Out)
. Vì kích thước stack bị giới hạn nên bạn chỉ nên tạo các biến cục bộ trong stack. Tuy nhiên, mảng có thể khá lớn khiến toàn bộ stack cạn kiệt rất nhanh. Đồng thời, việc thực thi chương trình được hoàn tất với một lỗi. Do đó, chúng ta nên mô tả các mảng ở cấp độ toàn cục là tĩnh (static
) hoặc phân bổ bộ nhớ cho chúng một cách động (điều này cũng được thực hiện từ heap
).