Array Dua Dimensi

Tata letak memori row-major, perulangan bersarang, dan keterkaitan array multidimensi dengan memori.

Daftar isi

Data berbentuk tabel, seperti jadwal piket atau nilai ujian beberapa siswa, lebih alami disimpan dalam array dua dimensi. Topik ini membahas deklarasi array dua dimensi, tata letak memorinya yang disebut row-major, serta pola perulangan bersarang untuk mengisi dan membacanya. Setelah membacanya, array multidimensi kecil dapat dibuat dan diproses sambil memahami susunan byte di balik tampilan tabelnya.

Deklarasi array dua dimensi#

Array dua dimensi dideklarasikan dengan dua pasang tanda kurung siku, yaitu jumlah baris terlebih dahulu lalu jumlah kolom. Deklarasi int matriks[2][3]; menyediakan enam elemen int yang tertata dalam dua baris berisi tiga kolom. Elemen diakses dengan menyebut indeks baris lalu indeks kolom, misalnya matriks[1][2] untuk baris kedua kolom ketiga.

c Deklarasi dan pengaksesan matriks
// tidak-dijalankan
int matriks[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

int x = matriks[1][2]; /* baris 1, kolom 2, bernilai 6 */

Tanda kurung kurawal bersarang pada inisialisasi memperjelas batas setiap baris, dan penulisannya boleh dirapatkan menjadi satu baris bila ukurannya kecil. Bentuk tanpa kurung kurawal bagian dalam, misalnya {1, 2, 3, 4, 5, 6}, juga sah dan diisi dengan urutan yang sama. Elemen yang tidak kebagian nilai awal otomatis berisi 0, sama seperti pada array satu dimensi.

Contoh nyata yang pas untuk struktur ini adalah jadwal piket seminggu. Tabel dengan lima hari dan dua shift dapat disimpan dalam int piket[5][2];, dan sel piket[3][0] menyimpan nomor petugas pada hari keempat shift pagi. Penamaan indeks yang jelas seperti ini membuat kode lebih mudah dibaca daripada satu array panjang berisi sepuluh angka yang urutannya harus diingat.

Tata letak memori row-major#

Enam elemen pada matriks di atas tidak tersimpan dalam kisi dua dimensi di memori, melainkan berderet pada enam sel int yang berurutan. Urutannya baris demi baris, dan susunan ini disebut row-major, yaitu baris mayor. Baris pertama menempati byte paling awal, disusul baris kedua, dan seterusnya sampai habis.

baris 0baris 1123456memori123456offset 048121620satu blokmemoriberurutan
Susunan row-major int matriks[2][3] dalam memori

Susunan ini membuat posisi setiap elemen dapat dihitung langsung dari indeksnya. Offset byte elemen baris ii kolom jj pada matriks berkolom CC adalah (i×C+j)×4(i \times C + j) \times 4 bila ukuran int 4 byte. Rumus itulah alasan jumlah kolom selalu wajib disebutkan pada deklarasi parameter matriks, sebagaimana dibahas pada topik Mengoper Array ke Fungsi.

Program berikut membuktikan hitungan tersebut dengan sizeof terhadap matriks, satu barisnya, dan satu elemennya.

c ukuran-matriks.c
#include <stdio.h>

int main(void) {
    int matriks[3][4];

    printf("Ukuran seluruh matriks : %zu byte\n", sizeof matriks);
    printf("Ukuran satu baris      : %zu byte\n", sizeof matriks[0]);
    printf("Ukuran satu elemen     : %zu byte\n", sizeof matriks[0][0]);
    return 0;
}
text Keluaran
Ukuran seluruh matriks : 48 byte
Ukuran satu baris      : 16 byte
Ukuran satu elemen     : 4 byte

Angka pada keluaran di atas cocok dengan hitungan manual, yaitu 48 byte sama dengan 3 × 4 × 4 dan 16 byte sama dengan 4 × 4. Satu baris terukur utuh karena matriks[0] sendiri adalah array satu dimensi berisi empat elemen.

Perulangan bersarang#

Mengolah seluruh isi matriks memerlukan dua perulangan yang bersarang, yaitu perulangan baris di luar dan perulangan kolom di dalam. Setiap satu putaran perulangan luar menuntaskan satu baris penuh. Program berikut mengisi setiap sel dengan hasil kali indeks baris dan kolomnya, lalu mencetak isi papan baris demi baris.

c isi-dan-cetak.c
#include <stdio.h>

int main(void) {
    int papan[3][4];

    /* mengisi setiap sel dengan hasil kali indeks baris dan kolomnya */
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            papan[i][j] = (i + 1) * (j + 1);
        }
    }

    /* mencetak isi papan baris demi baris */
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%4d", papan[i][j]);
        }
        printf("\n");
    }
    return 0;
}
text Keluaran
   1   2   3   4
   2   4   6   8
   3   6   9  12

Penentu format %4d membuat setiap angka dicetak dalam kolom selebar empat karakter, sehingga tabelnya rata. Urutan perulangan juga dapat dibalik, yaitu kolom di luar dan baris di dalam, dan hasilnya tetap benar. Meski begitu, urutan baris-di-luar lebih sejalan dengan tata letak row-major karena memori dibaca berurutan dari depan ke belakang.

Perulangan bersarang juga menuntut penempatan akumulator yang benar. Akumulator untuk seluruh sel dideklarasikan dan dinolkan di luar kedua perulangan, sedangkan akumulator khusus satu baris dinolkan di antara keduanya. Akumulator yang dinolkan di tempat yang salah membuat hasil penghitungan hanya mencakup sebagian data.

Array lebih dari dua dimensi#

Pola yang sama berlanjut ke dimensi yang lebih tinggi. Deklarasi int kubus[2][3][4]; menyediakan 24 elemen yang juga berderet berurutan dalam satu blok memori, dimulai dari menghabiskan indeks terakhir lebih dahulu. Pengaksesannya memakai tiga indeks, misalnya kubus[0][1][2].

c Array tiga dimensi
// tidak-dijalankan
int kubus[2][3][4];      /* 24 elemen, tiga indeks */
kubus[0][1][2] = 7;      /* mengisi satu elemen */

Array tiga dimensi kecil masih lazim untuk data seperti koordinat atau papan permainan sederhana, sedangkan dimensi yang lebih dalam jarang dipakai karena sulit dibaca. Jumlah elemennya adalah hasil kali ketiga dimensinya, yaitu 2 × 3 × 4, dan inisialisasinya memakai tiga tingkat tanda kurung kurawal dengan urutan yang sama seperti pengaksesannya. Aturan offset-nya berlanjut dengan pola yang sama. Offset byte elemen kubus[i][j][k] adalah ((i * 3 + j) * 4 + k) dikali ukuran elemennya, sehingga indeks terakhir selalu bertambah paling cepat. Apabila struktur datanya mulai rumit, mengelompokkan tiap potongan data ke dalam struct lebih mudah dirawat daripada menumpuk dimensi array.

Kesalahan umum#

  • Menukar urutan indeks menjadi matriks[kolom][baris]. Pada matriks tak persegi, penukaran ini menghasilkan indeks yang salah dan bisa keluar dari batas. Kebiasaan yang aman adalah memakai i untuk baris dan j untuk kolom secara konsisten di seluruh kode.
  • Menganggap matriks sebagai array dari pointer. Array dua dimensi tetap satu blok memori berurutan dan bukan int **, sehingga tidak dapat dioper begitu saja ke fungsi yang menerima int **.
  • Menyalin batas perulangan tanpa menyesuaikannya. Matriks berukuran 2×3 yang ditelusuri dengan dua perulangan yang sama-sama berhenti di 3 akan membaca baris ketiga yang tidak ada. Batas setiap tingkat perulangan harus mengikuti dimensinya masing-masing.
  • Melakukan perhitungan indeks linear tanpa mengalikan indeks baris dengan jumlah kolom. Apabila tabel 2×3 diratakan menjadi array satu dimensi berisi enam elemen, indeks baris i kolom j adalah i * 3 + j. Kelalaian mengalikan dengan jumlah kolom membuat elemen yang dituju melenceng.

Ringkasan#

  • Array dua dimensi dideklarasikan dengan jumlah baris lalu jumlah kolom, dan elemennya diakses dengan dua indeks.
  • Semua elemennya tersimpan dalam satu blok memori berurutan baris demi baris, yaitu tata letak row-major.
  • Pengisian dan pembacaannya memakai perulangan bersarang, dengan perulangan kolom di dalam dan baris di luar.
  • Offset byte elemen [i][j] pada matriks berkolom C dapat dihitung dengan (i*C + j) dikali ukuran elemen.

Referensi#