Idiom ini secara alami keluar dari alokasi larik 1D. Mari kita mulai dengan mengalokasikan array 1D dari beberapa tipe arbitrer T
:
T *p = malloc( sizeof *p * N );
Sederhana bukan? The ekspresi *p
memiliki tipe T
, sehingga sizeof *p
memberikan hasil yang sama seperti sizeof (T)
, jadi kita mengalokasikan cukup ruang untuk N
berbagai -element dari T
. Ini berlaku untuk semua tipeT
.
Sekarang, mari kita gantikan T
dengan tipe array seperti R [10]
. Kemudian alokasi kami menjadi
R (*p)[10] = malloc( sizeof *p * N);
Semantik di sini persis sama dengan metode alokasi 1D; semua yang berubah adalah jenis p
. Alih-alih T *
, sekarang R (*)[10]
. Ekspresi *p
memiliki tipe T
yaitu tipe R [10]
, jadi sizeof *p
ekuivalen dengan sizeof (T)
yang ekivalen dengan sizeof (R [10])
. Jadi kita mengalokasikan cukup ruang untuk N
oleh 10
elemen array R
.
Kita dapat mengambil ini lebih jauh jika kita mau; misalkan R
itu sendiri merupakan tipe array int [5]
. Gantikan itu R
dan kita dapatkan
int (*p)[10][5] = malloc( sizeof *p * N);
Kesepakatan yang sama - sizeof *p
sama dengan sizeof (int [10][5])
, dan kami akhirnya mengalokasikan sebagian besar memori yang berdekatan yang cukup besar untuk menampung N
oleh 10
oleh 5
array int
.
Jadi itulah sisi alokasi; bagaimana dengan sisi aksesnya?
Ingat bahwa []
operasi subskrip didefinisikan dalam istilah aritmatika penunjuk: a[i]
didefinisikan sebagai *(a + i)
1 . Jadi, operator subskrip []
secara implisit merujuk sebuah pointer. Jika p
penunjuk ke T
, Anda dapat mengakses nilai menunjuk ke baik dengan secara eksplisit dereferensi dengan *
operator unary :
T x = *p;
atau dengan menggunakan []
operator subskrip:
T x = p[0]; // identical to *p
Jadi, jika p
menunjuk ke elemen pertama dari sebuah array , Anda dapat mengakses elemen apa pun dari array itu dengan menggunakan subskrip pada penunjuk 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
Sekarang, mari kita lakukan operasi substitusi lagi dan ganti T
dengan tipe array 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];
Satu perbedaan yang langsung terlihat; kami secara eksplisit mendereferensi p
sebelum menerapkan operator subskrip. Kami tidak ingin subskrip ke p
, kami ingin subskrip ke p
poin apa (dalam hal ini, array arr[0]
). Sejak unary *
memiliki hak lebih rendah dari subscript []
operator, kita harus menggunakan tanda kurung secara eksplisit kelompok p
dengan *
. Tapi ingat dari atas itu *p
sama dengan p[0]
, jadi kita bisa menggantinya dengan
R x = (p[0])[i];
atau hanya
R x = p[0][i];
Jadi, jika p
menunjuk ke larik 2D, kita bisa mengindeks ke larik itu melalui p
seperti ini:
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
Mengambil kesimpulan yang sama seperti di atas dan menggantinya R
dengan 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];
Ini berfungsi sama jika p
menunjuk ke array biasa, atau jika menunjuk ke memori yang dialokasikan malloc
.
Idiom ini memiliki manfaat sebagai berikut:
- Sederhana - hanya satu baris kode, sebagai lawan dari metode alokasi sedikit demi sedikit
T **arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( size_t i = 0; i < N; i++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
}
}
- Semua baris dari array yang dialokasikan adalah * bersebelahan *, yang tidak terjadi dengan metode alokasi sepotong-sepotong di atas;
- Mendelokasi array semudah dengan satu panggilan ke
free
. Sekali lagi, tidak benar dengan metode alokasi sedikit demi sedikit, di mana Anda harus membatalkan alokasi masing-masing arr[i]
sebelum Anda dapat membatalkan alokasi arr
.
Terkadang metode alokasi sedikit demi sedikit lebih disukai, seperti saat heap Anda terfragmentasi dengan buruk dan Anda tidak dapat mengalokasikan memori sebagai potongan yang berdekatan, atau Anda ingin mengalokasikan larik "bergerigi" di mana setiap baris dapat memiliki panjang yang berbeda. Tetapi secara umum, ini adalah cara yang lebih baik untuk pergi.
1. Ingat bahwa array bukanlah pointer - sebaliknya, ekspresi array diubah menjadi ekspresi pointer jika perlu.