Menu

Pointer di Golang: & dan *, new, nil, dan Kapan Memakainya

Pointer menyimpan alamat sebuah nilai. Pelajari & dan *, new, pointer ke struct, kenapa aman mengembalikan pointer ke variabel lokal, kapan memakai pointer, dan panic nil pointer dereference.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

& dan *

Pointer menyimpan alamat memori sebuah nilai. &x memberimu pointer ke x. *p mengikuti pointer itu ke nilainya.

Tanda bintang punya dua peran. Di dalam tipe (*int), artinya "pointer ke int". Di dalam ekspresi (*p), artinya "nilai yang ditunjuk p". Mencetak p itu sendiri menampilkan alamat seperti 0xc000012345, yang berbeda setiap kali program dijalankan.

Kenapa pointer ada: berbagi alih-alih menyalin

Go mengirim semuanya sebagai nilai (pass by value). Sebuah fungsi mendapat salinan setiap argumen, jadi ia tidak bisa mengubah variabel milik pemanggil. Mengirim pointer berarti yang disalin adalah alamatnya, dan kedua sisi menjangkau nilai yang sama.

Go tidak punya reference dalam arti seperti di C++. "Pass by reference" di Go berarti mengirim pointer sebagai nilai.

Pointer ke struct

Pointer ke struct adalah pointer yang paling sering muncul di kode Go. Akses field melakukan dereference secara otomatis: p.Name berarti (*p).Name.

Method mengikuti logika yang sama: pointer receiver memungkinkan method mengubah struct-nya. Halaman method membahas pilihan antara value receiver dan pointer receiver.

new

new(T) mengalokasikan T yang bernilai nol lalu mengembalikan alamatnya. Hasilnya sama dengan mendeklarasikan variabel lalu mengambil alamatnya.

p := new(int) // *int pointing at 0
var x int
q := &x           // same thing, two lines
a := new(Account) // same as &Account{}

Dalam praktiknya, &T{...} lebih umum untuk struct karena kamu bisa mengisi field-nya di ekspresi yang sama. new berguna untuk pointer ke tipe dasar. Jangan tertukar dengan make, yang membuat slice, map, dan channel lalu mengembalikannya dalam keadaan siap pakai, bukan sebagai pointer.

Mengembalikan pointer ke variabel lokal itu aman

Di C, mengembalikan alamat variabel lokal adalah bug. Di Go, itu kode yang biasa saja.

Escape analysis milik compiler memutuskan apakah sebuah nilai tinggal di stack atau di heap. Kamu tidak pernah memilihnya, dan kamu tidak pernah membebaskan memori: garbage collector mengambilnya kembali saat tidak ada lagi yang menunjuk ke sana. go build -gcflags=-m mencetak keputusan-keputusan itu kalau kamu penasaran.

Pointer nil

Zero value dari tipe pointer apa pun adalah nil. Melakukan dereference pada nil akan memicu panic.

Output:

1
2
true
recovered: runtime error: invalid memory address or nil pointer dereference

Tanpa recover, program akan crash dengan pesan itu dan stack trace yang diawali panic: runtime error: invalid memory address or nil pointer dereference dan [signal SIGSEGV: segmentation violation ...]. Sumber yang biasa:

  • variabel pointer atau field struct yang tidak pernah diisi
  • m[key] pada map[string]*T untuk key yang tidak ada, yang mengembalikan nil
  • mengabaikan error: f, _ := os.Open(path) membiarkan f bernilai nil saat gagal
  • method dengan pointer receiver yang dipanggil pada pointer nil, lalu membaca sebuah field

Memanggil method pada pointer nil itu sendiri sah. Panic baru terjadi saat method itu menyentuh field milik receiver.

Kapan memakai pointer

Pakai pointer saat:

  • fungsi atau method harus mengubah nilai milik pemanggil
  • nilainya berupa struct besar dan sering dikirim ke sana kemari
  • tipenya tidak boleh disalin (berisi sync.Mutex, atau mewakili sumber daya unik seperti koneksi)
  • kamu perlu menyatakan "tidak ada nilai" dengan nil, misalnya field opsional di struct JSON (*int membedakan "tidak ada" dari 0)

Jangan pakai pointer saat:

  • nilainya kecil dan hanya dibaca (time.Time, sebuah Point)
  • tipenya sudah berupa nilai yang mirip referensi: slice, map, channel, fungsi, dan interface jarang butuh * di depannya. *[]int atau *map[string]int hampir selalu merupakan kesalahan

Pointer tidak otomatis lebih cepat. Menyalin struct kecil itu murah, dan pointer bisa memaksa alokasi di heap serta menambah pekerjaan garbage collector.

Pointer dan variabel loop

Mengambil alamat variabel loop aman sejak Go 1.22, karena setiap iterasi punya variabelnya sendiri:

Program ini mencetak 0 1 2. Sebelum Go 1.22 (yang juga memperkenalkan range 3), loop yang sama ditulis sebagai for i := 0; i < 3; i++ mencetak 3 3 3, karena ketiga pointer berbagi satu variabel. Perhatikan bahwa &v dalam for _, v := range items menunjuk ke salinan elemen, bukan elemen di dalam slice. Untuk mendapatkan pointer ke elemen itu sendiri, pakai &items[i].

Kesalahan umum

  • Melakukan dereference tanpa pemeriksaan. Periksa if p == nil di mana pun nil mungkin muncul.
  • Pointer ke nilai range. &v menunjuk ke salinan; pakai &s[i].
  • Pointer ke slice dan map. Jarang dibutuhkan. Kembalikan slice yang baru saja.
  • Menyimpan &s[i] melewati append. Kalau append mengalokasikan ulang, pointer itu masih merujuk ke array lama dan penulisan berikutnya tidak terlihat di mana pun.

Pertanyaan yang sering diajukan

Apa arti & dan * di Go?

&x mengambil alamat x dan menghasilkan pointer bertipe *T. *p melakukan dereference pada pointer: membaca atau menulis nilai yang ditunjuknya. Di dalam sebuah tipe, *T berarti "pointer ke T".

Apa perbedaan new dan make di Go?

new(T) mengalokasikan T yang bernilai nol dan mengembalikan *T; fungsi ini bisa dipakai untuk tipe apa pun. make hanya bisa dipakai untuk slice, map, dan channel, dan mengembalikan nilai bertipe itu yang sudah diinisialisasi (bukan sekadar nol, dan bukan pointer): make(map[string]int) siap dipakai, sedangkan new(map[string]int) adalah pointer ke map nil.

Bolehkah mengembalikan pointer ke variabel lokal di Go?

Boleh. Berbeda dengan C, ini aman. Escape analysis milik compiler melihat bahwa variabel itu hidup lebih lama daripada fungsinya, lalu mengalokasikannya di heap. func newInt() *int { x := 5; return &x } adalah kode Go yang benar.

Apa penyebab "invalid memory address or nil pointer dereference" di Go?

Membaca atau menulis lewat pointer nil: variabel *T yang tidak pernah diisi, pencarian key yang tidak ada di map[K]*V, atau fungsi yang mengembalikan nil bersama error yang tidak kamu periksa. Periksa nil, atau inisialisasi pointer sebelum dipakai.

Apakah Go punya aritmetika pointer?

Tidak. Kamu tidak bisa menambahkan angka ke pointer atau mengakses memori dengan indeks lewat pointer. Package unsafe mengizinkannya untuk kode tingkat rendah, tanpa jaminan keamanan apa pun. Kode Go biasa memakai slice untuk memori yang bersambung.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai