Array dan pointer punya hubungan yang sangat erat sampai-sampai keduanya sering dipertukarkan dalam kode sehari-hari. Ketika nama array dipakai dalam ekspresi, array otomatis “meluruh” (decay) menjadi pointer ke elemen pertamanya — inilah alasan arr[i] bisa juga ditulis sebagai *(arr + i). Namun array dan pointer tetap dua entitas berbeda, dan perbedaannya baru terasa jelas saat array dikirim ke fungsi. Chapter ini menuntaskan ketiganya: aturan decay, kesetaraan notasi akses, dan konsekuensi praktisnya di batas fungsi.

Array Decay Menjadi Pointer #

Aturan decay menyatakan: dalam hampir semua ekspresi, nama array dikonversi secara implisit menjadi &arr[0] — pointer ke elemen pertama. Ada dua pengecualian besar: ketika array jadi operand sizeof dan ketika array di-&. Di luar itu, nama array “berperan” sebagai pointer.

cpp
#include <iostream>

// Array dikirim sebagai pointer + ukuran terpisah
void cetakArray(const int* arr, int ukuran) {
    for (int i = 0; i < ukuran; ++i) {
        if (i > 0) std::cout << " ";
        std::cout << arr[i];
    }
    std::cout << "\n";
}

int main() {
    int data[5] = {2, 4, 6, 8, 10};

    int* p = data;  // nama array meluruh menjadi pointer ke elemen pertama

    std::cout << "p == &data[0] : " << (p == &data[0]) << "\n";
    std::cout << "data[2]       : " << data[2] << "\n";
    std::cout << "*(p + 2)      : " << *(p + 2) << "\n";

    // Array dan pointer adalah dua hal berbeda di level ukuran
    std::cout << "sizeof(data)  : " << sizeof(data) << " byte\n";
    std::cout << "sizeof(p)     : " << sizeof(p) << " byte\n";

    cetakArray(data, 5);

    return 0;
}

Output yang Dihasilkan:

text
p == &data[0] : 1
data[2]       : 6
*(p + 2)      : 6
sizeof(data)  : 20 byte
sizeof(p)     : 8 byte
2 4 6 8 10

Mari kita bedah:

  1. Baris 15 (int* p = data;): tidak perlu menulis &data[0] — decay membuat data otomatis menjadi pointer ke elemen pertama. Bandingkan p == &data[0] yang bernilai true di output.
  2. Baris 17-18 (data[2] vs *(p + 2)): keduanya menghasilkan nilai yang sama, 6. Bahasa C++ sebenarnya mendefinisikan arr[i] sebagai *(arr + i): “mulai dari alamat elemen pertama, maju i elemen, lalu dereference”. Notasi kurung siku hanyalah gula sintaksis.
  3. Baris 21-22 (sizeof): inilah bukti array dan pointer bukan hal yang sama. Di dalam main(), data masih dikenali sebagai array utuh sehingga sizeof(data) = 20 byte (5 × 4), sedangkan sizeof(p) = 8 byte karena p hanyalah sebuah alamat.
  4. Baris 24 (cetakArray(data, 5)): array meluruh lagi — fungsi menerima pointer ke data[0], bukan salinan array. Fungsi bekerja langsung pada array asli.

Konsekuensi saat Array Masuk ke Fungsi #

Deklarasi void cetakArray(const int* arr, int ukuran) menyimpan satu fakta penting: di dalam fungsi, array sudah tidak pernah utuh — yang sampai hanya alamat elemen pertamanya. Karena itu fungsi tidak tahu berapa panjang arraynya; sizeof(arr) di dalam cetakArray akan bernilai 8 (ukuran pointer), bukan 20. Dari sini lahir konvensi yang kita pakai sampai sekarang: ukuran dikirim sebagai parameter terpisah.

cpp Ilustrasi: sizeof di dalam fungsi
void periksa(int arr[]) {           // int arr[] identik dengan int* arr
    // sizeof(arr) di sini = 8 (ukuran pointer), BUKAN ukuran array asli
}

Parameter int arr[] hanyalah ejaan lain untuk int* arr — compiler memperlakukan keduanya sama persis. Konsekuensi kedua: karena fungsi menerima alamat asli, perubahan elemen di dalam fungsi mengubah array asli di pemanggil, berbeda dengan pass-by-value biasa yang hanya menyalin. Konsekuensi ketiga: batas array ikut hilang. Fungsi yang menerima int* dan ukuran salah bisa membaca atau menulis melampaui array — bencana klasik yang dibahas pada Section Keamanan dan Undefined Behavior.

Kata const pada parameter const int* arr di atas berjanji bahwa fungsi tidak akan mengubah isi array; variasi penempatan const pada pointer kita bedah khusus pada Chapter Const Pointer dan Pointer to Const.

Batas Decay dan Alternatif Modern #

Decay hanya terjadi pada array C-style (int data[5]). Kontainer modern seperti std::vector dan std::array adalah objek utuh yang tahu panjangnya sendiri — vec.size() selalu benar dan std::vector tidak meluruh. Untuk kode baru, kebanyakan kebutuhan array lebih baik dilayani std::vector seperti yang kita pelajari pada Chapter std::vector di Section Array dan String. Pointer dan array mentah tetap perlu kita kuasai karena kode lama, API C, dan mekanisme di balik kontainer semuanya memakainya.

Dengan pemahaman decay, hubungan array-pointer kita sudah kokoh. Langkah berikutnya adalah mengeksplorasi aritmetikanya secara sadar: apa yang sebenarnya terjadi ketika pointer +1, dan bagaimana menghitung jarak antarpointer.

Referensi: cppreference — array declaration (decay) , cppreference — pointer .