Setiap variable menempati sebuah lokasi di memori komputer dan lokasi tersebut punya alamat. Kebanyakan kode Go bekerja dengan nilai variable tanpa pernah peduli alamatnya, tetapi ada kalanya kita butuh alamat tersebut: memodifikasi variable milik pemanggil dari dalam fungsi, atau menghindari penyalinan data yang besar. Go menyediakan dua operator untuk level ini — & mengambil alamat sebuah variable, dan * membaca serta menulis nilai pada alamat yang ditunjuk. Pada chapter ini kita memakai keduanya dalam satu program utuh dan mengenali keadaan khusus pointer nil.
Dua Operator: & dan * #
Tabel berikut merangkum peran kedua operator beserta tempat kemunculannya:
| Penulisan | Nama | Peran |
|---|---|---|
&x | Operator alamat | Menghasilkan alamat variable x, bertipe pointer *int bila x bertipe int |
*p | Operator dereference | Membaca atau menulis nilai yang berada di lokasi yang ditunjuk pointer p |
var p *int | Deklarasi tipe | *int adalah tipe “pointer ke int”; bintang di sini bagian dari nama tipe, bukan operator |
Perhatikan bahwa tanda * mempunyai dua peran berbeda. Pada deklarasi var p *int, tanda bintang adalah bagian dari penulisan tipe pointer. Pada ekspresi *p, tanda bintang adalah operator dereference yang “mengikuti” alamat sampai ke nilainya. Program berikut menunjukkan ketiga peran tersebut:
package main
import "fmt"
// naikkan menambah satu pada nilai yang ditunjuk pointer p.
func naikkan(p *int) {
*p = *p + 1
}
func main() {
x := 10
p := &x // operator & mengambil alamat variable x
fmt.Println("nilai x :", x)
fmt.Println("nilai *p :", *p)
*p = 25 // operator * menulis ke lokasi yang ditunjuk p
fmt.Println("nilai x :", x)
naikkan(&x) // alamat x dikirim ke fungsi
fmt.Println("nilai x :", x)
var ptr *int // zero value pointer adalah nil
fmt.Println("ptr nil? :", ptr == nil)
}Output yang Dihasilkan:
nilai x : 10
nilai *p : 10
nilai x : 25
nilai x : 26
ptr nil? : trueMari kita telaah baris demi baris program di atas:
- Baris 6-8 (
func naikkan(p *int)): Parameterpbertipe*int, sehingga fungsi ini menerima alamat sebuah bilangan bulat. Statement*p = *p + 1membaca nilai di alamat tersebut, menjumlahkan satu, lalu menulis hasilnya kembali ke alamat yang sama. - Baris 11 (
x := 10): Variablexdibuat dengan nilai 10; runtime menyimpan nilai ini di sebuah alamat memori. - Baris 12 (
p := &x): Operator&menghasilkan alamat milikx, lalu alamat itu disimpan pada variablepyang bertipe*int. Sejak baris ini,pdanxmerujuk ke lokasi memori yang sama. - Baris 14 (
fmt.Println("nilai *p :", *p)): Dereference*pmembaca nilai di alamat yang ditunjuk, yaitu 10. Membaca*pmemberikan hasil yang sama dengan membacax. - Baris 16 (
*p = 25): Assignment melalui*pmenulis 25 ke lokasi memori milikx. Baris berikutnya membuktikanxikut berubah menjadi 25 meskipun statement ini tidak menyebut namaxsama sekali. - Baris 19 (
naikkan(&x)): Alamatxdikirim sebagai argumen. Go mengopi nilai argumen seperti biasa, tetapi yang dikopi adalah alamatnya, sehingga fungsi tetap menulis ke lokasi asli danxmenjadi 26. Inilah cara standar membuat fungsi mengubah variable milik pemanggil. - Baris 22-23 (
var ptr *int): Pointer yang dideklarasikan tanpa nilai awal berada pada zero valuenil. Perbandinganptr == nilmenghasilkantrue.
Mencetak Alamat dengan %p #
Package fmt menyediakan verb %p untuk mencetak alamat pointer, misalnya fmt.Printf("%p\n", &x). Namun nilai alamat yang tercetak berbeda pada setiap eksekusi program — bahkan pada program yang sama dijalankan dua kali — karena sistem operasi menempatkan memori secara dinamis. Contoh-contoh pada chapter ini karenanya sengaja hanya mencetak nilai hasil dereference *p yang selalu pasti, bukan alamat pointer yang terus berubah. Jika Anda mencoba sendiri dengan %p, angka heksadesimal seperti 0xc000014098 yang muncul memang wajar berbeda dari komputer lain.
Referensi #
- A Tour of Go: Basics — tur resmi yang memperkenalkan pointer, dereference, dan struct.
- Dokumentasi package fmt
— daftar verb format termasuk
%puntuk alamat pointer. - Go FAQ — jawaban resmi mengapa Go memilih tanpa aritmetika pointer.
Kemampuan mengambil alamat dan dereference membuka topik yang lebih dalam. Section Pointer dan Memory akan membedah perbedaan pointer dan value secara menyeluruh, termasuk kapan memilih salah satunya. Sebelum sampai di sana, kita perlu menuntaskan satu keterampilan dasar terakhir pada ekspresi: membaca urutan evaluasi operator, yang dibahas pada chapter Prioritas Operator .
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
pointer.go
Unduh berkaspackage main
import "fmt"
// naikkan menambah satu pada nilai yang ditunjuk pointer p.
func naikkan(p *int) {
*p = *p + 1
}
func main() {
x := 10
p := &x // operator & mengambil alamat variable x
fmt.Println("nilai x :", x)
fmt.Println("nilai *p :", *p)
*p = 25 // operator * menulis ke lokasi yang ditunjuk p
fmt.Println("nilai x :", x)
naikkan(&x) // alamat x dikirim ke fungsi
fmt.Println("nilai x :", x)
var ptr *int // zero value pointer adalah nil
fmt.Println("ptr nil? :", ptr == nil)
}