Operator address-of (&) adalah pintu gerbang menuju dunia pointer. Tanpa operator ini, kita tidak punya cara untuk memperoleh alamat sebuah variabel dan memberikannya ke pointer. Operator & berperan sebagai jembatan antara dunia variabel bernama dan dunia alamat memori yang menjadi ruang kerja pointer. Pada chapter ini kita akan memahami bagaimana & bekerja, kapan operator ini boleh dipakai, dan kapan tidak—termasuk jebakan klasik memakai & pada ekspresi yang bukan lvalue atau pada variabel yang sudah keluar dari scope.
Cara Kerja Operator & #
Operator & adalah operator unary yang menghasilkan alamat operand. Operand harus berupa lvalue—yaitu ekspresi yang merujuk ke region memori yang bisa ditulis, misalnya nama variabel, elemen array, atau hasil dereference pointer. Operator & sendiri menghasilkan rvalue bertipe T * jika operand bertipe T. Jadi &x bertipe int * bila x bertipe int, dan &arr[i] bertipe int * bila arr bertipe int[].
Mekanisme kerjanya sederhana: saat compiler mengevaluasi &nilai, compiler tidak membaca nilai nilai melainkan mengembalikan alamat memori tempat nilai disimpan. Hasilnya bisa langsung diberikan ke pointer, dicetak dengan %p, atau dilewatkan ke fungsi yang menerima pointer. Penting untuk diingat bahwa & menghasilkan alamat region operand—tidak melakukan dereference apa pun, dan tidak memodifikasi operand.
Contoh berikut memperlihatkan penggunaan & pada dua variabel bertipe berbeda, lalu menyimpan hasilnya pada pointer bertipe yang sesuai.
#include <stdio.h>
int main(void) {
int nilai = 100;
long besar = 999999L;
/* & menghasilkan alamat operand */
int *pi = &nilai;
long *pl = &besar;
printf("nilai = %d di alamat %p\n", nilai, (void*)&nilai);
printf("besar = %ld di alamat %p\n", besar, (void*)&besar);
printf("pi = %p (menunjuk nilai)\n", (void*)pi);
printf("pl = %p (menunjuk besar)\n", (void*)pl);
return 0;
}Output yang Dihasilkan:
nilai = 100 di alamat 0x7ffd3116f7bc
besar = 999999 di alamat 0x7ffd3116f7b0
pi = 0x7ffd3116f7bc (menunjuk nilai)
pl = 0x7ffd3116f7b0 (menunjuk besar)Alamat yang tersimpan di pi persis sama dengan &nilai—kita baru saja menyalin alamat tersebut dari &nilai ke variabel pi saat inisialisasi. Begitu pula pl dan &besar. Tipe pointer pi adalah int * agar compiler tahu bahwa 4 byte pada alamat tersebut harus ditafsirkan sebagai int; tipe pl adalah long * untuk 8 byte long. Mencoba menyimpan &nilai ke long * akan menimbulkan peringatan -Wincompatible-pointer-types dan biasanya menandakan bug.
Mari kita telaah baris-baris kode di atas:
- Baris 4-5 (
int nilai = 100; long besar = 999999L;): dua variabel bertipe berbeda. SuffixLmenandakan literallongagar kompatibel dengan tipebesar. - Baris 8-9 (
int *pi = &nilai; long *pl = &besar;):&nilaimenghasilkanint *yang disalin kepi;&besarmenghasilkanlong *yang disalin kepl. Tipe kiri dan kanan harus cocok, jika tidak compiler memperingatkan. - Baris 11-12 (
printf("... di alamat %p", (void*)&nilai);): cetak alamat langsung dari&nilai. Cast(void*)diperlukan karena%pmengharapkanvoid *(ISO/IEC 9899 §7.21.6.1). - Baris 13-14 (
printf("pi = %p ...", (void*)pi);): cetak nilai pointerpi, yang merupakan salinan dari&nilai. Karena keduanya menunjuk ke tempat yang sama, outputnya identik dengan baris 11.
& Mengikat Variabel ke Fungsi #
Salah satu kegunaan paling penting & adalah meneruskan alamat variabel lokal ke fungsi lain. Tanpa &, fungsi hanya menerima salinan nilai parameter dan tidak bisa mengubah variabel pemanggil. Dengan &, fungsi menerima alamat variabel dan bisa membaca/menulis memori pemanggil secara langsung.
#include <stdio.h>
void tampilkan_alamat(int *param) {
printf("Di dalam fungsi: alamat param = %p, nilai = %d\n",
(void*)param, *param);
}
int main(void) {
int x = 77;
printf("Di dalam main : alamat x = %p, nilai = %d\n",
(void*)&x, x);
/* Meneruskan alamat x ke fungsi */
tampilkan_alamat(&x);
return 0;
}Output yang Dihasilkan:
Di dalam main : alamat x = 0x7fff8b1a05ac, nilai = 77
Di dalam fungsi: alamat param = 0x7fff8b1a05ac, nilai = 77Alamat x di main dan alamat yang diterima parameter param di fungsi persis sama. Itulah inti dari pass-by-pointer: tidak ada duplikasi nilai variabel x; fungsi bekerja langsung pada memori yang sama. Inilah dasar untuk out-parameter, modifikasi variabel pemanggil, dan efisiensi passing struct/array besar—topik yang akan kita perdalam di chapter Pointer sebagai Parameter.