Array adalah struktur data paling dasar: kumpulan elemen bertipe sama yang tersimpan berurutan di memori. Banyak algoritma klasik — sorting, searching, prefix sum — memodelkan input sebagai array. C++ mewarisi array gaya C yang sangat efisien namun punya banyak keterbatasan. Pada Chapter ini kita akan mempelajari cara mendeklarasikan dan mengakses C-style array, lalu memahami mengapa di DSA modern kita lebih memilih std::vector atau std::array.

Deklarasi dan Akses #

C-style array dideklarasikan dengan tipe dan ukuran konstan dalam tanda kurung siku. Indeks dimulai dari 0. Elemen diakses dengan array[indeks].

Mekanisme: Penyimpanan Berurutan di Memori #

Elemen array C diletakkan berurutan di memori tanpa celah. Untuk int data[5] di mana sizeof(int) == 4, total memori yang dipakai adalah 20 byte. Alamat data[i] sama dengan data + i * sizeof(int). Inilah sebabnya akses data[i] sangat cepat: satu operasi perkalian dan penambahan alamat.

Kode: Deklarasi dan Iterasi Array #

cpp
#include <iostream>

int main() {
    int data[5] = {10, 20, 30, 40, 50};

    std::cout << "data[0] = " << data[0] << '\n';
    std::cout << "data[4] = " << data[4] << '\n';

    // Ukuran array gaya lama
    std::size_t ukuran = sizeof(data) / sizeof(data[0]);
    std::cout << "ukuran  = " << ukuran << '\n';

    // std::size sejak C++17
    std::cout << "size    = " << std::size(data) << '\n';

    long long total = 0;
    for (std::size_t i = 0; i < std::size(data); ++i) {
        total += data[i];
    }
    std::cout << "total   = " << total << '\n';
    return 0;
}

Output yang Dihasilkan:

text
data[0] = 10
data[4] = 50
ukuran  = 5
size    = 5
total   = 150
  1. Baris int data[5] = {10, 20, 30, 40, 50};: deklarasi array dengan 5 elemen dan inisialisasi nilai. Bila kita menulis int data[5] = {10, 20};, sisanya otomatis 0.
  2. Baris data[0]: akses elemen pertama. Indeks 0 karena alamat base sudah menunjuk elemen pertama tanpa offset.
  3. Baris sizeof(data) / sizeof(data[0]): trik klasik untuk memperoleh ukuran array — total byte dibagi byte per elemen. Hanya bekerja di scope deklarasi, bukan setelah array dilewatkan ke fungsi.
  4. Baris std::size(data): cara C++17 yang lebih aman dan ekspresif.

Array Multidimensi #

Array multidimensi dideklarasikan dengan dua pasang kurung siku, misalnya int matriks[3][4]. Penyimpanan tetap berurutan (row-major): seluruh baris pertama, lalu baris kedua, dst.

Kode: Matriks 3x3 #

cpp
#include <iostream>

int main() {
    int m[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

    // Akses elemen diagonal utama
    std::cout << "diagonal:";
    for (int i = 0; i < 3; ++i) {
        std::cout << ' ' << m[i][i];
    }
    std::cout << '\n';

    // Jumlah semua elemen
    long long total = 0;
    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            total += m[i][j];
    std::cout << "total    = " << total << '\n';
    return 0;
}

Output yang Dihasilkan:

text
diagonal: 1 5 9
total    = 45
  1. Baris int m[3][3] = {{1,2,3}, ...};: matriks 3x3 dengan inisialisasi baris-per-baris. Setiap {} dalam mewakili satu baris.
  2. Baris m[i][i]: elemen diagonal utama — indeks baris sama dengan indeks kolom.
  3. Loop bersarang: loop luar untuk baris, loop dalam untuk kolom. Total iterasi 3 * 3 = 9 kali.

Keterbatasan: Array Decay #

Ketika array C-style dilewatkan ke fungsi, tipenya meluruh menjadi pointer ke elemen pertama. Akibatnya, fungsi tidak tahu ukuran asli array, dan sizeof di dalam fungsi hanya mengembalikan ukuran pointer.

Mekanisme: Pointer Decay #

Parameter int arr[] di signature fungsi sebenarnya diinterpretasikan sebagai int* arr. Semua informasi ukuran hilang. Inilah alasan fungsi C klasik seperti std::qsort meminta parameter tambahan berupa jumlah elemen.

Kode: Ukuran yang Hilang #

cpp
#include <iostream>

void cetak(int arr[]) {
    // sizeof(arr) di sini = sizeof(int*), bukan ukuran array!
    std::cout << "sizeof di fungsi = " << sizeof(arr) << '\n';
    std::cout << "tidak tahu panjang asli\n";
}

int main() {
    int data[5] = {1, 2, 3, 4, 5};
    std::cout << "sizeof di main   = " << sizeof(data) << '\n';
    cetak(data);
    return 0;
}

Output yang Dihasilkan:

text
sizeof di main   = 20
sizeof di fungsi = 8
tidak tahu panjang asli
  1. Baris sizeof(data) di main: data masih berupa array — 5 * sizeof(int) = 20 byte.
  2. Baris sizeof(arr) di cetak: arr sudah meluruh menjadi int* — ukurannya 8 byte (pointer di sistem 64-bit), bukan ukuran array asli.
  3. Akibatnya, fungsi penerima array C-style harus diberi tahu ukuran secara terpisah melalui parameter lain.

Batas Ukuran Statis #

C-style array dialokasikan di stack bila dideklarasikan secara lokal. Stack berukuran terbatas (biasanya 1–8 MB per thread), sehingga array besar seperti int data[10'000'000] dapat memicu stack overflow.

Mengapa Beralih ke std::vector dan std::array #

Tabel berikut merangkum alasan perpindahan dari C-style array ke container STL:

AspekC-style arraystd::array<T,N>std::vector<T>
UkuranTetap, di stackTetap, di stackDinamis, di heap
Tahu ukuran sendiriHanya di scope asalYa (size())Ya (size())
Aman dari decayTidakYaYa
Bisa di-copy dengan =Tidak langsungYaYa
Iterator STLTidak langsungYaYa

std::array adalah pembungkus tipis di atas array C dengan ukuran tetap — pilihan ideal bila ukuran diketahui saat kompilasi dan kita ingin antarmuka yang konsisten dengan STL. std::vector menyediakan ukuran dinamis dengan antarmuka yang sama.

Ringkasan #

  • C-style array efisien dan ringan, tetapi ukurannya tetap dan tidak tahu panjangnya sendiri setelah decay.
  • sizeof(arr) / sizeof(arr[0]) atau std::size(arr) hanya bekerja di scope deklarasi.
  • Array multidimensi disimpan dalam row-major order.
  • Array decay menjadi pointer saat dilewatkan ke fungsi — informasi ukuran hilang.
  • Untuk DSA modern, utamakan std::vector (dinamis) atau std::array (statis) yang aman dan konsisten dengan STL.