Operator dereference (*) adalah pasangan kerja dari operator address-of (&). Jika & mengambil alamat sebuah variabel, operator * melakukan kebalikannya: diberikan sebuah pointer, operator * membaca atau menulis nilai pada alamat yang ditunjuk. Tanpa operator *, pointer hanya berisi angka alamat yang tidak berguna—kita tidak bisa membaca atau memodifikasi nilai di alamat tersebut. Pada chapter ini kita akan menelusuri dua peran operator *: sebagai read (di sisi kanan penugasan) dan sebagai write (di sisi kiri penugasan), serta bagaimana * berinteraksi dengan pointer arithmetic.

Peran Operator *: Baca dan Tulis #

Operator * adalah operator unary yang menghasilkan lvalue—ekspresi yang merujuk ke region memori yang ditunjuk pointer. Karena hasilnya lvalue, kita bisa memakainya di kedua sisi penugasan: di sisi kanan sebagai read (membaca nilai di alamat), atau di sisi kiri sebagai write (menulis nilai ke alamat). Tipe nilai yang dihasilkan *p ditentukan oleh tipe pointer p: jika p bertipe int *, maka *p bertipe int; jika p bertipe double *, maka *p bertipe double.

Mekanisme kerjanya: saat compiler melihat *p di sisi kanan, compiler membaca sizeof(*p) byte di alamat yang tersimpan pada variabel p, lalu menafsirkan byte-byte tersebut sebagai tipe yang ditunjuk. Saat compiler melihat *p = 99 di sisi kiri, compiler menulis 99 ke alamat yang sama, juga dengan jumlah byte yang sesuai dengan tipe. Inilah sebabnya tipe pointer penting: tipe menentukan berapa byte yang dibaca/ditulis dan bagaimana byte-byte tersebut ditafsirkan.

Visualisasi Konsep #

Diagram berikut memperlihatkan dua peran operator *: sebagai read di sisi kanan penugasan dan sebagai write di sisi kiri penugasan:

Pada diagram di atas, kedua peran operator * bertumpu pada alamat yang sama—alamat angka yang tersimpan di p. Pada peran read, operator *p mengikuti alamat di p, membaca 4 byte (untuk int), dan menghasilkan nilai yang kemudian disalin ke variabel salinan. Pada peran write, operator *p menjadi lvalue di sisi kiri, dan nilai 99 ditulis ke alamat yang sama sehingga variabel angka ikut berubah. Karena alamat yang dirujuk p adalah alamat angka, kedua peran operator * bekerja pada region memori yang sama.

c
#include <stdio.h>

int main(void) {
    int angka = 25;
    int *p = &angka;

    /* Baca nilai melalui dereference */
    int salinan = *p;
    printf("angka = %d, *p = %d, salinan = %d\n", angka, *p, salinan);

    /* Tulis nilai melalui dereference */
    *p = 99;
    printf("Setelah *p = 99 -> angka = %d, *p = %d\n", angka, *p);

    return 0;
}

Output yang Dihasilkan:

text
angka = 25, *p = 25, salinan = 25
Setelah *p = 99 -> angka = 99, *p = 99

Pada baris int salinan = *p;, operator * membaca nilai int di alamat p (yaitu alamat angka) dan menyalinnya ke variabel salinan. Pada baris *p = 99;, operator * menulis nilai 99 ke alamat yang ditunjuk p. Karena alamat tersebut adalah alamat angka, perubahan langsung terlihat saat kita mencetak angka. Dengan kata lain, angka dan *p adalah dua cara berbeda untuk mengakses region memori yang sama.

Mari kita telaah baris-baris kode di atas:

  1. Baris 4 (int angka = 25;): variabel angka bertipe int dengan nilai awal 25. Compiler memesan 4 byte di stack untuk angka.
  2. Baris 5 (int *p = &angka;): pointer p bertipe int * dan menyimpan alamat angka. Inisialisasi ini hanya menyalin alamat, tidak membaca atau menulis nilai angka.
  3. Baris 8 (int salinan = *p;): operator * membaca nilai int pada alamat p. Hasilnya disalin ke variabel salinan. Sekarang salinan bernilai 25, terpisah dari angka.
  4. Baris 11 (*p = 99;): operator * menghasilkan lvalue yang menunjuk ke region memori angka. Penugasan = 99 menulis nilai 99 ke region tersebut. Setelah baris ini, angka == 99.
  5. Baris 12 (printf("... angka = %d, *p = %d", angka, *p);): dua ekspresi yang merujuk ke memori yang sama—angka secara langsung, *p melalui pointer—menghasilkan nilai yang sama: 99.

Dereference pada Pointer yang Bergerak #

Operator * juga bisa diterapkan pada ekspresi pointer hasil pointer arithmetic. Ekspresi *(p + 1) artinya: ambil alamat p, geser maju satu elemen (sesuai tipe), lalu dereference alamat baru tersebut. Inilah dasar dari akses elemen array melalui pointer—ekspresi arr[i] setara dengan *(arr + i) (ISO/IEC 9899 §6.5.2.1).

c
#include <stdio.h>

int main(void) {
    int data[3] = {5, 10, 15};
    int *p = data;   /* p menunjuk ke data[0] */

    /* Dereference berbagai posisi pointer */
    printf("*p       = %d (data[0])\n", *p);
    printf("*(p + 1) = %d (data[1])\n", *(p + 1));
    printf("*(p + 2) = %d (data[2])\n", *(p + 2));

    /* Modifikasi elemen via dereference */
    *(p + 1) = 222;
    printf("Setelah *(p+1) = 222 -> data[1] = %d\n", data[1]);

    return 0;
}

Output yang Dihasilkan:

text
*p       = 5 (data[0])
*(p + 1) = 10 (data[1])
*(p + 2) = 15 (data[2])
Setelah *(p+1) = 222 -> data[1] = 222

Ekspresi *(p + 1) di sisi kiri penugasan menulis ke elemen kedua array. Karena p + 1 adalah alamat data[1], dereference-nya menjadi lvalue yang menunjuk ke memori data[1]. Penugasan = 222 langsung mengubah array aslinya. Inilah mengapa arr[i] dan *(arr + i) dianggap ekuivalen secara semantik—keduanya menghasilkan lvalue yang menunjuk ke elemen ke-i dari array.