Hubungan antara pointer dan array di C begitu erat hingga keduanya kadang dianggap sinonim—meskipun sebenarnya tidak. Nama sebuah array bisa dipakai seolah-olah pointer ke elemen pertamanya, dan operator subscript [] didefinisikan dalam bentuk pointer arithmetic. Selain itu, saat array dilewatkan ke fungsi, array “meluruh” (decay) menjadi pointer biasa, sehingga fungsi penerima tidak lagi mengetahui ukuran array aslinya. Pada chapter ini kita akan membuktikan kesetaraan arr[i] dengan *(arr + i), serta mendemonstrasikan fenomena array decay yang sering menjadi sumber bug bagi pemula.

Kesetaraan arr[i] dan *(arr + i) #

Standar C (ISO/IEC 9899 §6.5.2.1) mendefinisikan ekspresi arr[i] sebagai singkatan dari *(arr + i). Artinya: tambahkan offset i ke pointer arr (yang sebenarnya adalah alamat elemen pertama), lalu dereference hasilnya. Konsekuensi yang menarik: ekspresi i[arr] juga valid dan setara dengan arr[i], karena penjumlahan bersifat komutatif (arr + i == i + arr). Sama halnya, &arr[i] identik dengan arr + i keduanya menghasilkan alamat elemen ke-i.

Mekanisme kerjanya: saat compiler melihat arr[3], compiler mengubah ekspresi ini menjadi *(arr + 3). Jika arr adalah int[5], nama arr dalam konteks ini decay menjadi int * yang menunjuk ke elemen pertama. Pointer arithmetic maju 3 elemen (3 * 4 byte = 12 byte), lalu dereference membaca nilai int di alamat tersebut. Karena arr + 3 == 3 + arr, ekspresi 3[arr] juga menghasilkan hal yang sama secara matematis—meskipun menulis seperti itu tidak disarankan demi keterbacaan.

Visualisasi Konsep #

Diagram berikut memperlihatkan bagaimana ekspresi arr[3], *(arr + 3), dan 3[arr] semuanya menunjuk ke elemen yang sama:

Pada diagram di atas, ketiga ekspresi akses arr[3], *(arr + 3), dan 3[arr] konvergen ke elemen yang sama. Nama array arr decay menjadi pointer ke elemen pertama (0x...0a4), lalu pointer arithmetic menambahkan offset 3 elemen (= 12 byte untuk int) untuk mencapai alamat 0x...0b0. Dereference pada alamat ini menghasilkan nilai elemen ke-3. Komutativitas penjumlahan menjadikan 3[arr] setara dengan arr[3] secara matematis, meskipun idiom 3[arr] tidak disarankan untuk keterbacaan.

c
#include <stdio.h>

int main(void) {
    int arr[5] = {11, 22, 33, 44, 55};

    /* arr[i] setara dengan *(arr + i) */
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d, *(arr + %d) = %d, eq = %s\n",
               i, arr[i], i, *(arr + i),
               (arr[i] == *(arr + i)) ? "ya" : "tidak");
    }

    /* &arr[i] setara dengan arr + i */
    printf("\n&arr[2]    = %p\n", (void*)&arr[2]);
    printf("arr + 2    = %p\n", (void*)(arr + 2));
    printf("Sama?      %s\n",
           ((void*)&arr[2] == (void*)(arr + 2)) ? "ya" : "tidak");

    /* Idiom komutatif: arr[i] == i[arr] */
    printf("\narr[3]   = %d\n", arr[3]);
    printf("3[arr]   = %d (sama!)\n", 3[arr]);

    return 0;
}

Output yang Dihasilkan:

text
arr[0] = 11, *(arr + 0) = 11, eq = ya
arr[1] = 22, *(arr + 1) = 22, eq = ya
arr[2] = 33, *(arr + 2) = 33, eq = ya
arr[3] = 44, *(arr + 3) = 44, eq = ya
arr[4] = 55, *(arr + 4) = 55, eq = ya

&arr[2]    = 0x7fff7a3e95b8
arr + 2    = 0x7fff7a3e95b8
Sama?      ya

arr[3]   = 44
3[arr]   = 44 (sama!)

Lima baris pertama membuktikan arr[i] == *(arr + i) untuk semua i. Baris alamat menunjukkan &arr[2] == arr + 2. Dan keajaiban terakhir: 3[arr] == arr[3] == 44. Memang agak aneh secara visual, tetapi secara semantik benar: 3[arr] diubah menjadi *(3 + arr), sama seperti arr[3] menjadi *(arr + 3). Hanya saja, menulis 3[arr] dalam kode produksi akan membingungkan pembaca—pertahankan gaya arr[i] demi keterbacaan.

Mari kita telaah baris-baris kode di atas:

  1. Baris 4 (int arr[5] = {11, 22, 33, 44, 55};): array 5 elemen bertipe int. Array ini tidak “adalah” pointer, tetapi namanya bisa decay menjadi pointer.
  2. Baris 7-10 (for (... arr[i] ...)): bandingkan arr[i] dan *(arr + i) untuk setiap indeks. Keduanya selalu sama persis.
  3. Baris 13-15 (&arr[2] vs arr + 2): alamat elemen ke-2 dari array. Kedua ekspresi menghasilkan pointer ke elemen yang sama.
  4. Baris 18-19 (arr[3] vs 3[arr]): idiom komutatif. Karena arr + 3 == 3 + arr, ekspresi 3[arr] valid dan menghasilkan nilai yang sama.

Array Decay Saat Dilewatkan ke Fungsi #

Inilah jebakan terbesar bagi pemula C: array kehilangan informasi ukurannya saat dilewatkan ke fungsi. Parameter fungsi bertipe int arr[] atau int arr[5] sebenarnya diubah oleh compiler menjadi int *arr—pointer biasa. Akibatnya, sizeof(arr) di dalam fungsi menghasilkan ukuran pointer (8 byte pada sistem 64-bit), bukan ukuran array aslinya. Fenomena ini disebut array decay.

c
#include <stdio.h>

/* Demontrasi array decay ke pointer */
void cetak_ukuran(int *param) {
    printf("Di dalam fungsi: sizeof(param) = %zu byte\n", sizeof(param));
}

int main(void) {
    int data[5] = {1, 2, 3, 4, 5};

    printf("Di main: sizeof(data)     = %zu byte (ukuran array)\n", sizeof(data));
    printf("Di main: sizeof(data[0])  = %zu byte (ukuran elemen)\n", sizeof(data[0]));
    printf("Di main: jumlah elemen    = %zu\n", sizeof(data) / sizeof(data[0]));

    /* Pass ke fungsi: array decay ke pointer */
    cetak_ukuran(data);
    printf("\nSetelah lewat ke fungsi, kita tidak bisa pakai sizeof untuk\n");
    printf("mendapat jumlah elemen asli - informasi itu hilang saat decay.\n");

    return 0;
}

Output yang Dihasilkan:

text
Di main: sizeof(data)     = 20 byte (ukuran array)
Di main: sizeof(data[0])  = 4 byte (ukuran elemen)
Di main: jumlah elemen    = 5
Di dalam fungsi: sizeof(param) = 8 byte

Setelah lewat ke fungsi, kita tidak bisa pakai sizeof untuk
mendapat jumlah elemen asli - informasi itu hilang saat decay.

Di main, sizeof(data) menghasilkan 20 (5 elemen * 4 byte). Namun setelah array dilewatkan ke fungsi, sizeof(param) hanya menghasilkan 8—ukuran pointer int *. Idiom sizeof(arr) / sizeof(arr[0]) yang menghasilkan jumlah elemen hanya berfungsi pada scope di mana array asli masih hidup; di dalam fungsi penerima, idiom ini menghasilkan jawaban salah (misalnya 8/4 = 2 alih-alih 5). Inilah mengapa fungsi yang menerima array biasanya memerlukan parameter tambahan int n untuk panjang array.