Array bergaya C dan std::array

Array mentah yang perlu dikenali dan pembungkus ukuran tetap yang lebih disarankan.

Daftar isi

Topik ini membuka modul data berurutan dengan dua bentuk array yang berbeda tingkat keamanannya. Array bergaya C adalah warisan bahasa C yang perlu dikenali karena masih banyak muncul pada kode lama, sedangkan std::array adalah pembungkus pustaka standar berukuran tetap yang lebih disarankan untuk kode C++ baru. Setelah membaca topik ini, cara menghitung jumlah elemen array mentah, batas keamanannya, dan keunggulan pembungkus standar sudah dapat diuraikan.

Array bergaya C: deklarasi dan jumlah elemen#

Array bergaya C (C-style array) menyimpan sejumlah elemen bertipe sama dalam satu blok memori yang berurutan. Deklarasinya menuliskan ukuran setelah nama, sehingga int nilai[5] menyatakan lima elemen bertipe int dengan indeks 0 sampai 4. Isi array dapat ditetapkan sejak deklarasi melalui daftar inisialisasi di dalam kurung kurawal. Apabila daftar inisialisasi dipakai, elemen yang tidak diberi nilai secara eksplisit otomatis bernilai 0. Setelah dideklarasikan, ukurannya tidak dapat bertambah atau berkurang.

Program berikut menghitung rata-rata lima nilai ujian. Banyaknya elemen dihitung dengan pembagian sizeof(nilai) / sizeof(nilai[0]), yaitu jumlah byte seluruh array dibagi jumlah byte satu elemennya. Dengan pembagian ini, angka lima cukup ditulis satu kali pada deklarasi, sehingga perubahan ukuran hanya dilakukan di satu tempat.

cpp rata-rata-array-c.cpp
#include <iostream>

int main() {
    int nilai[5] = {80, 75, 90, 60, 85};
    int jumlah = 0;
    int banyak = sizeof(nilai) / sizeof(nilai[0]);

    for (int i = 0; i < banyak; ++i) {
        jumlah += nilai[i];
    }

    std::cout << "Banyak nilai: " << banyak << '\n';
    std::cout << "Jumlah: " << jumlah << '\n';
    std::cout << "Rata-rata: " << static_cast<double>(jumlah) / banyak << '\n';
    return 0;
}
text Keluaran
Banyak nilai: 5
Jumlah: 390
Rata-rata: 78

Pembagian sizeof tersebut bekerja karena compiler mengetahui ukuran asli array pada lingkup tempat array itu dideklarasikan. Meskipun begitu, pola ini bukan informasi ukuran yang melekat pada array, karena array bergaya C tidak menyimpan panjangnya di mana pun. Angka di dalam kurung siku hanya menentukan alokasi memori dan tidak dibawa bersama array saat array dikirim ke bagian program lain.

Titik lemah array bergaya C#

Kelemahan pertama array bergaya C adalah ukurannya yang tidak tersimpan pada array itu sendiri. Fungsi yang menerima array harus diberi tahu banyaknya elemen melalui parameter terpisah. Alasannya, begitu array dikirim ke fungsi, array berperilaku seperti pointer, dan pembagian sizeof di atas tidak lagi memberikan hasil yang benar pada fungsi penerima. Perilaku penurunan ini (decay) dibedah lebih rinci pada modul pointer dan memori.

Kelemahan kedua adalah tidak adanya pemeriksaan batas indeks. Mengakses elemen pada indeks di luar array tidak menghasilkan error kompilasi dan sering tidak menampilkan pesan apa pun saat program berjalan. Perilaku tersebut termasuk undefined behavior, yakni program boleh berperilaku apa pun tanpa jaminan, misalnya menimpa variabel lain atau berhenti mendadak. Contoh berikut sengaja tidak dijalankan karena berada di luar jaminan aturan bahasa.

cpp Contoh: akses di luar batas (tidak dijalankan)
// tidak-dijalankan (akses di luar batas adalah undefined behavior)
#include <iostream>

int main() {
    int nilai[5] = {80, 75, 90, 60, 85};
    nilai[9] = 100; // indeks 9 berada di luar array dan tidak dipemeriksa
    std::cout << nilai[9] << '\n';
    return 0;
}

Kelemahan ketiga adalah array bergaya C tidak dapat disalin atau dibandingkan secara utuh dengan operator biasa. Menyalin seluruh isinya ke array lain harus ditulis sebagai perulangan manual, sedangkan == pada dua array tidak membandingkan isinya. Ketiga kelemahan ini berasal dari status array bergaya C sebagai fitur tingkat memori mentah yang diwarisi dari C.

std::array: pembungkus ukuran tetap yang lebih aman#

std::array adalah pembungkus (wrapper) array berukuran tetap dari header <array>. Deklarasinya menyebutkan tipe elemen dan ukurannya, misalnya std::array<int, 5>. Objeknya menyimpan elemen berurutan seperti array bergaya C, tetapi dilengkapi member function seperti size(), at(), dan fill(), serta mendukung range-based for tanpa pola tambahan apa pun.

Program berikut mengulang penghitungan rata-rata nilai ujian dengan std::array. Ukurannya dibaca dari nilai.size(), sedangkan akses pada indeks 9 sengaja dipicu untuk memperlihatkan penolakan yang jelas. Member function at() melempar exception bertipe std::out_of_range apabila indeksnya di luar batas, dan blok try serta catch menangkapnya agar program tetap berjalan.

cpp rata-rata-std-array.cpp
#include <array>
#include <iostream>
#include <stdexcept>

int main() {
    std::array<int, 5> nilai = {80, 75, 90, 60, 85};
    int jumlah = 0;

    for (int n : nilai) {
        jumlah += n;
    }

    std::cout << "Banyak nilai: " << nilai.size() << '\n';
    std::cout << "Rata-rata: " << static_cast<double>(jumlah) / nilai.size() << '\n';

    try {
        int diIndeksSembilan = nilai.at(9);
        std::cout << "Nilai pada indeks 9: " << diIndeksSembilan << '\n';
    } catch (const std::out_of_range& e) {
        std::cout << "Akses indeks 9 ditolak karena di luar batas\n";
    }
    return 0;
}
text Keluaran
Banyak nilai: 5
Rata-rata: 78
Akses indeks 9 ditolak karena di luar batas

Pesan penolakan tersebut kontras dengan array bergaya C yang diam-diam menerima akses di luar batas. Selain itu, std::array dapat disalin dengan satu penugasan, dibandingkan isinya dengan ==, dan dikirim ke fungsi melalui reference tanpa kehilangan informasi ukurannya. Ketika objeknya berpindah ke fungsi, penerima tetap dapat membaca size() tanpa parameter tambahan apa pun. Biayanya sama dengan array bergaya C karena elemennya tetap tersimpan berurutan tanpa alokasi tambahan.

Memilih antara array bergaya C dan std::array#

Tabel berikut merangkum perbedaan kedua bentuk pada aspek yang paling sering berpengaruh.

AspekArray bergaya Cstd::array
Ukuran tersimpanTidak, dihitung dengan sizeofYa, melalui size()
Akses di luar batasUndefined behaviorat() melempar std::out_of_range
Penyalinan utuhPerulangan manualSatu penugasan
Perbandingan isiTidak tersedia== membandingkan isi
Pengiriman ke fungsiMenurun menjadi pointerTetap utuh melalui reference

Ketika jumlah elemen memang tetap dan sudah diketahui saat kompilasi, std::array memberikan jaminan yang lebih lengkap tanpa kehilangan kinerja, sehingga menjadi pilihan awal yang disarankan. Array bergaya C tetap perlu dikenali karena banyak muncul pada kode warisan (legacy), antarmuka pustaka C, dan materi pointer. Untuk jumlah elemen yang baru diketahui saat program berjalan, std::vector lebih tepat daripada keduanya. Perbedaan biaya antara keduanya tidak ada, sehingga keputusan ini murni soal jaminan keamanan, bukan kinerja.

Kesalahan umum#

Kesalahan pada array berukuran tetap biasanya menyangkut batas indeks dan informasi ukuran. Tiga kesalahan berikut paling sering muncul.

  1. Pola sizeof yang dipindahkan ke dalam fungsi. Parameter array diterima sebagai pointer, sehingga pembagiannya menghasilkan ukuran pointer, bukan banyaknya elemen. Kirim ukurannya sebagai parameter terpisah atau gunakan std::array.
  2. Menghitung indeks terakhir sebagai nilai[5] pada array lima elemen. Indeks terakhirnya adalah 4, sehingga akses indeks 5 berada di luar batas dan berujung pada undefined behavior.
  3. Membandingkan dua array bergaya C dengan ==. Operator itu tidak membandingkan isinya, sehingga kesetaraan harus diuji elemen demi elemen, atau keduanya diganti dengan std::array.

Ketiganya berbagi satu akar penyebab, yaitu informasi ukuran dan batas yang tidak melekat pada array mentah. Kebiasaan yang sehat adalah menanyakan satu hal lebih dahulu, yaitu apakah datanya benar-benar berukuran tetap sejak awal. Apabila ukurannya memang tetap, std::array menutup ketiga kesalahan di atas sekaligus tanpa biaya tambahan apa pun.

Ringkasan#

  • Array bergaya C menyimpan elemen berurutan tanpa menyimpan ukurannya sendiri, sehingga ukurannya hanya dapat dihitung dengan sizeof pada lingkup deklarasinya.
  • Akses indeks di luar batas array bergaya C tidak dipemeriksa sama sekali dan berujung pada undefined behavior.
  • std::array menyediakan ukuran yang melekat, penyalinan, perbandingan, dan pemeriksaan batas melalui at(), dengan biaya penyimpanan yang sama dengan array mentah.

Referensi#