Pointer adalah variabel, dan variabel apa pun memiliki alamat — termasuk pointer itu sendiri. Lalu alamat pointer disimpan di mana? Di variabel lain yang bertipe pointer ke pointer, ditulis int** untuk pointer yang menunjuk pointer ke int. Dereference-nya pun bertingkat: satu * menghasilkan pointer, dua ** menghasilkan nilai akhirnya. Topik ini sering dianggap rumit, padahal mekanismenya persis pointer biasa — hanya dilakukan dua kali. Chapter ini membedah deklarasinya, dua kegunaan klasiknya, dan kapan kita memang butuh menyusunnya sampai dua lapis.
Deklarasi dan Dereference Bertingkat #
Kita bangun rantainya pelan-pelan: x adalah int, p adalah int* yang menunjuk x, dan pp adalah int** yang menunjuk p. Membaca nilai x dari pp butuh dua kali dereference: **pp.
#include <iostream>
// Mengubah pointer milik caller melalui pointer-to-pointer
void arahkan(int** pp, int* target) {
*pp = target;
}
int main() {
int a = 15;
int b = 40;
int* p = &a;
int** pp = &p; // pp menunjuk pointer p
std::cout << "*p : " << *p << "\n";
std::cout << "**pp : " << **pp << "\n";
**pp = 16; // mengubah a lewat dua lapis dereference
std::cout << "a setelah **pp = 16 : " << a << "\n";
arahkan(&p, &b);
std::cout << "p sekarang menunjuk b : " << *p << "\n";
std::cout << "**pp setelah pindah : " << **pp << "\n";
// Array of pointers: kumpulan pointer dalam satu array
int* daftar[3] = {&a, &b, &a};
std::cout << "daftar: ";
for (int i = 0; i < 3; ++i) {
if (i > 0) std::cout << " ";
std::cout << *daftar[i];
}
std::cout << "\n";
return 0;
}Output yang Dihasilkan:
*p : 15
**pp : 15
a setelah **pp = 16 : 16
p sekarang menunjuk b : 40
**pp setelah pindah : 40
daftar: 16 40 16Mari kita telusuri:
- Baris 12 (
int** pp = &p;):padalah variabel (bertipeint*), jadi&pmenghasilkan alamatnya, dan tipe yang cocok menampung alamatint*adalahint**. Rantainya:pp→p→a. - Baris 15-16 (
*pvs**pp):*psatu lapis dereference menghasilkan nilaia(15).**ppdua lapis:*ppdulu menghasilkanp, lalu*p-nya menghasilkana. Keduanya sampai ke nilai yang sama lewat panjang rantai yang berbeda. - Baris 18-19 (
**pp = 16;): dua lapis dereference di sisi kiri menulis ke objek paling ujung rantai —a. Kita mengubahatanpa menyebuta,p, maupunxapa pun selainpp. - Baris 21-23 (
arahkan(&p, &b);): kegunaan klasik pertama. Fungsiarahkanmenerimaint**, lalu*pp = target;mengganti isi pointer milik pemanggil. Ingat Chapter Pointer sebagai Parameter: mengirimpsaja hanya menyalin alamat; untuk mengubah variabelpitu sendiri, kita wajib mengirim alamatnya —&p— yang bertipeint**. - Baris 27-31 (
daftar): kegunaan klasik kedua — array of pointers. Setiap elemendaftaradalahint*biasa, dan*daftar[i]membaca nilai yang ditunjuknya. Pemilihan&a, &b, &asengaja menunjukkan bahwa elemen pointer boleh menunjuk objek yang sama.
Setelah arahkan dijalankan, output menegaskan efek gandanya: p menunjuk b sehingga *p bernilai 40, dan karena pp menunjuk p, ikutan **pp juga berubah menjadi 40 — rantai otomatis mengikuti.
Kapan Pointer to Pointer Dibutuhkan #
Meskipun mekanismenya penting dipahami, int** jarang muncul di kode C++ modern:
- Mengubah pointer di dalam fungsi: pola
arahkan(&p, ...)di atas adalah kasus klasiknya; C++ juga menyediakan reference ke pointer (int*&) — perluasan pass-by-reference pada Section Fungsi — untuk peran yang sama dengan tanda&yang lebih sedikit. - Array of pointers: kumpulan
int*untuk tabel look-up atau array 2D dinamis gaya C; di C++ modern,std::vector<std::vector<int>>ataustd::vector<int*>menyembunyikan hampir semua kebutuhan ini. - Interoperabilitas dengan API C: banyak fungsi C mengirim hasil lewat parameter
T**, jadi kita tetap akan membacanya saat memakai library warisan.
Tiga lapis (int***) dan seterusnya sah secara sintaksis, tetapi kehadirannya hampir selalu sinyal desain yang perlu direvisi.
Rantai pointer bertingkat selesai. Selanjutnya kita periksa satu pointer yang justru sengaja tidak tahu tipenya: void*, pointer serbaguna warisan bahasa C yang masih kita temui di banyak API standar.
Referensi: cppreference — pointer declaration , cppreference — function declaration .