Banyak struktur data (linked list, tree, graph) memerlukan kita untuk menunjuk ke memori lain secara eksplisit. Pointer dan reference adalah dua sarana C++ untuk tujuan tersebut — keduanya menerjemahkan ke alamat memori, tetapi dengan sintaks dan jaminan yang berbeda. Pada Chapter ini kita akan mempelajari pointer, reference, pointer arithmetic, dan bagaimana pemilihan mode pass (value vs reference vs pointer) menentukan apakah fungsi bisa memodifikasi data pemanggil.
Pointer: Alamat sebagai Variabel #
Pointer adalah variabel yang menyimpan alamat memori. Kita mendeklarasikannya dengan T*, mengambil alamat variabel biasa dengan &, dan mengakses nilai yang ditunjuk dengan dereference *.
Mekanisme: Alamat dan Dereference #
Ketika kita menulis int a = 10; int* p = &a;, compiler memesan memori untuk a (4 byte), lalu memesan memori terpisah untuk p yang berisi alamat a. Mengakses *p berarti membaca nilai pada alamat yang tersimpan di p — yang sama dengan a.
Kode: Pointer dan Dereference #
#include <iostream>
int main() {
int a = 10;
int* p = &a; // p menyimpan alamat a
std::cout << "a = " << a << '\n';
std::cout << "*p = " << *p << '\n';
std::cout << "p = " << p << '\n';
*p = 25; // ubah nilai a lewat pointer
std::cout << "a = " << a << '\n';
return 0;
}Output yang Dihasilkan:
a = 10
*p = 10
p = 0x7ffdce2c157c
a = 25- Baris
int* p = &a;:&amengambil alamata.psekarang menunjuk kea. - Baris
*p: operator dereference — akses nilai pada alamat yang tersimpan dip, yaitu nilaia(10). - Baris
*p = 25;: menulis25ke alamatp, yang sama dengan mengubah nilaia. Setelah baris ini,amenjadi25.
Reference: Alias untuk Variabel #
Reference (T&) adalah alias — nama lain untuk variabel yang sudah ada. Setelah diinisialisasi, reference tidak bisa di- rebind ke variabel lain, dan tidak bisa null.
Kode: Reference sebagai Alias #
#include <iostream>
int main() {
int a = 10;
int& r = a; // r adalah alias a
std::cout << "r = " << r << '\n';
r = 50; // mengubah a
std::cout << "a = " << a << '\n';
return 0;
}Output yang Dihasilkan:
r = 10
a = 50- Baris
int& r = a;:rmenjadi nama lain untuka. Tidak ada memori tambahan untukr— keduanya menunjuk slot yang sama. - Baris
r = 50;: menulis50ke slot yang sama dengana. Setelah baris ini,abernilai50.
Pass by Value vs Reference vs Pointer #
Tiga cara melewatkan parameter memberi tiga jaminan berbeda. Pemilihan memengaruhi apakah fungsi bisa memodifikasi argumen pemanggil, dan apakah pemanggil wajib menangani null.
| Mode | Sintaks | Modifikasi? | Bisa null? |
|---|---|---|---|
| By value | int x | Tidak | Tidak berlaku |
| By reference | int& x | Ya | Tidak |
| By pointer | int* p | Ya (lewat deref) | Ya |
Kode: Tiga Mode Pass #
#include <iostream>
void ubah_value(int x) { // copy; tidak memengaruhi pemanggil
x = 99;
}
void ubah_ref(int& x) { // reference; memengaruhi pemanggil
x = 99;
}
void ubah_ptr(int* p) { // pointer; memengaruhi pemanggil
if (p != nullptr) *p = 99;
}
int main() {
int x = 1, y = 1, z = 1;
ubah_value(x);
ubah_ref(y);
ubah_ptr(&z);
std::cout << "x,y,z = " << x << ',' << y << ',' << z << '\n';
return 0;
}Output yang Dihasilkan:
x,y,z = 1,99,99ubah_value(x):xdisalin ke parameter. Modifikasi di dalam fungsi tidak memengaruhixdimain→ tetap1.ubah_ref(y): parameter adalah aliasy. Modifikasi langsung mengubahy→ menjadi99.ubah_ptr(&z): alamatzdilewatkan. Fungsi dereference untuk mengubah nilaiz→ menjadi99. Pengecekannullptrmelindungi dari kasus pemanggilanubah_ptr(nullptr).
Pointer Arithmetic #
Pointer ke elemen array mendukung operasi aritmetika: p + n maju n elemen, p - n mundur n elemen, p++ ke elemen berikutnya. Operasi ini adalah dasar iterator C++.
Mekanisme: Skala Sesuai Tipe #
p + 1 untuk int* sebenarnya menambah alamat sebanyak sizeof(int) byte (4 byte), bukan 1 byte. Compiler secara otomatis mengalikan offset dengan ukuran tipe yang ditunjuk.
Kode: Pointer Arithmetic pada Array #
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* it = arr; // menunjuk elemen pertama
std::cout << "*it = " << *it << '\n';
std::cout << "*(it+2) = " << *(it + 2) << '\n';
++it; // maju satu elemen
std::cout << "*it now = " << *it << '\n';
return 0;
}Output yang Dihasilkan:
*it = 10
*(it+2) = 30
*it now = 20- Baris
int* it = arr;: nama arrayarrmeluruh menjadi pointer ke elemen pertama.itmenunjukarr[0]. - Baris
*(it + 2): alamat maju 2 elemen (= 8 byte untukint), lalu dereference →arr[2] = 30. - Baris
++it: memajukanitsatu elemen. Sekarang*itsama denganarr[1] = 20.
Null Pointer dan Keamanan #
Pointer bisa bernilai nullptr — penanda bahwa pointer tidak menunjuk ke mana-mana. Dereference nullptr adalah undefined behavior (umumnya segmentation fault).
Kode: Cek Null Sebelum Dereference #
#include <iostream>
int main() {
int* p = nullptr;
if (p == nullptr) {
std::cout << "p null\n";
} else {
std::cout << *p << '\n'; // tidak aman!
}
return 0;
}Output yang Dihasilkan:
p null- Baris
int* p = nullptr;:nullptradalah literal null modern (sejak C++11) yang menggantikanNULLatau0. - Baris
if (p == nullptr): pengecekan eksplisit sebelum dereference. Tanpa pengecekan, program bisa crash.
Return by Reference #
Fungsi bisa mengembalikan reference, sehingga pemanggil dapat memodifikasi objek asli langsung. Pola ini dipakai oleh operator[] pada std::vector dan std::map.
Kode: Akses Elemen via Return Reference #
#include <iostream>
#include <vector>
int& akses_elemen(std::vector<int>& v, std::size_t i) {
return v[i];
}
int main() {
std::vector<int> data{1, 2, 3};
akses_elemen(data, 1) = 100; // modifikasi langsung
std::cout << "data[1] = " << data[1] << '\n';
return 0;
}Output yang Dihasilkan:
data[1] = 100- Baris
int& akses_elemen(...): return typeint&— reference ke elemen, bukan copy. - Baris
akses_elemen(data, 1) = 100;: hasil pemanggilan fungsi adalah reference, sehingga= 100menulis langsung kedata[1]. Tanpa&, assignment hanya mengubah copy lokal yang langsung dihancurkan.
Ringkasan #
- Pointer menyimpan alamat; reference adalah alias yang tidak bisa null dan tidak bisa di-rebind.
- Dereference pointer dengan
*, ambil alamat dengan&. - Pilih mode pass: by value untuk data kecil read-only, by const reference untuk data besar read-only, by reference/pointer untuk modifikasi.
- Pointer arithmetic otomatis diskalakan dengan
sizeof(T). - Cek
nullptrsebelum dereference; gunakannullptrliteral modern, bukanNULL. - Return by reference memungkinkan pola
f(x) = valueseperti padaoperator[]STL.