Thành ngữ này tự nhiên nằm ngoài phân bổ mảng 1D. Hãy bắt đầu với việc cấp phát một mảng 1D của một số kiểu tùy ý T
:
T *p = malloc( sizeof *p * N );
Đơn giản, phải không? Các biểu hiện *p
có kiểu T
, vì vậy sizeof *p
cho kết quả tương tự như sizeof (T)
, vì vậy chúng tôi đang phân bổ đủ không gian cho một N
mảng -element của T
. Điều này đúng cho bất kỳ loại nàoT
.
Bây giờ, hãy thay thế T
bằng một kiểu mảng như R [10]
. Sau đó, phân bổ của chúng tôi trở thành
R (*p)[10] = malloc( sizeof *p * N);
Ngữ nghĩa ở đây hoàn toàn giống với phương pháp cấp phát 1D; tất cả những gì đã thay đổi là loại p
. Thay vì T *
, nó là bây giờ R (*)[10]
. Biểu thức *p
có kiểu T
là kiểu R [10]
, vì vậy sizeof *p
tương đương với sizeof (T)
tương đương với sizeof (R [10])
. Vì vậy, chúng tôi đang phân bổ đủ không gian cho một N
bằng 10
mảng yếu tố R
.
Chúng ta có thể tiến xa hơn nữa nếu chúng ta muốn; giả sử R
bản thân nó là một kiểu mảng int [5]
. Thay thế cho R
và chúng tôi nhận được
int (*p)[10][5] = malloc( sizeof *p * N);
Tương tự đối phó - sizeof *p
cũng giống như sizeof (int [10][5])
, và chúng tôi gió lên bố trí một đoạn tiếp giáp bộ nhớ đủ lớn để tổ chức một N
bằng 10
bởi 5
mảng int
.
Vì vậy, đó là phía phân bổ; những gì về phía truy cập?
Hãy nhớ rằng các []
hoạt động subscript được định nghĩa về con trỏ số học: a[i]
được định nghĩa là *(a + i)
1 . Do đó, toán tử chỉ số phụ []
ngầm định tham chiếu đến một con trỏ. Nếu p
là con trỏ tới T
, bạn có thể truy cập giá trị trỏ đến bằng cách tham chiếu rõ ràng với toán *
tử một ngôi:
T x = *p;
hoặc bằng cách sử dụng []
toán tử chỉ số dưới:
T x = p[0]; // identical to *p
Do đó, nếu p
trỏ đến phần tử đầu tiên của một mảng , bạn có thể truy cập bất kỳ phần tử nào của mảng đó bằng cách sử dụng chỉ số con trên con trỏ p
:
T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p
Bây giờ, hãy thực hiện lại thao tác thay thế và thay thế T
bằng kiểu mảng R [10]
:
R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];
Một sự khác biệt rõ ràng ngay lập tức; chúng tôi đang tham khảo rõ ràng p
trước khi áp dụng toán tử chỉ số con. Chúng tôi không muốn chỉ số dưới vào p
, chúng tôi muốn chỉ số phụ vào những gì p
trỏ đến (trong trường hợp này là mảng arr[0]
). Vì một ngôi *
có quyền ưu tiên thấp hơn []
toán tử chỉ số con, chúng ta phải sử dụng dấu ngoặc đơn để nhóm rõ ràng p
với *
. Nhưng hãy nhớ từ phía trên, *p
nó giống như p[0]
, vì vậy chúng ta có thể thay thế nó bằng
R x = (p[0])[i];
hoặc chỉ
R x = p[0][i];
Do đó, nếu p
trỏ đến một mảng 2D, chúng ta có thể lập chỉ mục vào mảng đó thông qua p
như sau:
R x = p[i][j]; // access the i'th element of arr through pointer p;
// each arr[i] is a 10-element array of R
Lấy điều này đến kết luận tương tự như trên và thay thế R
bằng int [5]
:
int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];
Điều này hoạt động giống nhau nếu p
trỏ đến một mảng thông thường hoặc nếu nó trỏ đến bộ nhớ được cấp phát qua malloc
.
Thành ngữ này có những lợi ích sau:
- Nó đơn giản - chỉ một dòng mã, trái ngược với phương pháp phân bổ từng phần
T **arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( size_t i = 0; i < N; i++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
}
}
- Tất cả các hàng của mảng được phân bổ là * liền kề *, điều này không đúng với phương pháp phân bổ từng phần ở trên;
- Việc phân bổ mảng cũng dễ dàng bằng một lệnh gọi tới
free
. Một lần nữa, không đúng với phương pháp phân bổ từng phần, trong đó bạn phải phân bổ từng thứ arr[i]
trước khi có thể phân bổ arr
.
Đôi khi phương pháp phân bổ từng phần được ưu tiên hơn, chẳng hạn như khi heap của bạn bị phân mảnh nghiêm trọng và bạn không thể phân bổ bộ nhớ của mình dưới dạng một đoạn liền kề hoặc bạn muốn phân bổ một mảng "răng cưa" trong đó mỗi hàng có thể có độ dài khác nhau. Nhưng nói chung, đây là cách tốt hơn để đi.
1. Hãy nhớ rằng mảng không phải là con trỏ - thay vào đó, biểu thức mảng được chuyển đổi thành biểu thức con trỏ khi cần thiết.