Artikel ini ditulis oleh ruangan tutorial bahasa go untuk memperkenalkan kepada anda dalam keadaan bagaimana penunjuk Go (Go Pointer) digunakan, saya harap ia dapat membantu rakan-rakan memerlukan!
Penggunaan petunjuk dalam kod GO tidak mesra dengan orang baru, terutamanya apabila sukar untuk membezakan senario penggunaan.
Saya fikir salah satu salah tanggapan terbesar apabila menggunakan penunjuk ialah penunjuk dalam Go sangat serupa dengan penunjuk dalam C. Namun, itu tidak berlaku. Penunjuk tidak berfungsi dalam Go dengan cara yang sama seperti dalam C/C.
Artikel ini akan membincangkan cara menggunakan penunjuk Go dengan betul.
Adalah diterima umum bahawa aplikasi akan berjalan lebih pantas apabila menggunakan penunjuk kerana ia mengelak daripada menyalin nilai sepanjang masa. Tidak menghairankan bahawa dalam Go kami mempunyai idea yang sama.
Walau bagaimanapun, hantaran penunjuk dalam Go biasanya lebih perlahan daripada hantaran nilai. Ini adalah akibat daripada Go menjadi bahasa kutipan sampah. Apabila anda menghantar penuding kepada fungsi, Go perlu melakukan analisis melarikan diri untuk menentukan sama ada pembolehubah harus disimpan pada timbunan atau timbunan. Ini sudah menambah beberapa overhed tambahan, tetapi sebaliknya pembolehubah boleh disimpan dalam timbunan. Apabila anda menyimpan pembolehubah dalam timbunan, anda juga kehilangan masa semasa GC sedang melaksanakan.
Ciri mudah Go ialah anda boleh menyemak tindakan analisis melarikan diri dengan melaksanakan arahan go build -gcflags="-m"
. Jika anda melakukan ini, Go akan memberitahu anda jika pembolehubah terlepas ke timbunan:
./main.go:44:20: greet ... argument does not escape ./main.go:44:21: greeting escapes to heap ./main.go:44:21: name escapes to heap
Jika pembolehubah tidak terlepas ke timbunan, ia berada pada timbunan. Tindanan tidak memerlukan pemungut sampah untuk mengosongkan pembolehubah, ia hanya melakukan operasi push/pop
.
Jika mana-mana kandungan diluluskan mengikut nilai, ia akan sentiasa diproses pada timbunan, yang tidak akan menyebabkan kutipan sampah di atas kepala. (GC akan dijalankan secara lalai. Kurang kandungan dalam timbunan bermakna GC kurang perlu dilakukan).
Sekarang anda tahu bahawa menggunakan penunjuk akan mengurangkan prestasi, jadi bilakah anda perlu menggunakan penunjuk?
Adakah penunjuk sentiasa berprestasi lebih teruk daripada pemindahan nilai? Itu jelas tidak berlaku. Penunjuk memainkan peranan apabila bekerja dengan struktur data yang besar. Ini boleh menyebabkan kos kutipan sampah diimbangi oleh kos penyalinan sejumlah besar data.
Apabila saya menyebut perkara ini, saya selalu ditanya 'berapa besarkah data besar itu'?
Saya rasa tiada nilai tetap di sini, apa-apa yang berkaitan dengan prestasi harus ditanda aras. Go mempunyai alat penanda aras berkuasa terbina dalam yang boleh dieksploitasi sepenuhnya
Satu-satunya cara untuk mengubah suai parameter fungsi ialah dengan menghantar penunjuk. Secara lalai, pengubahsuaian kepada nilai dilakukan pada salinan. Oleh itu pengubahsuaian ini tidak boleh dicerminkan dalam fungsi yang memanggilnya.
Lihat kod berikut:
type person struct { name string }func main() { p := person{"Richard"} rename(p) fmt.Println(p) }func rename(p person) { p.name = "test" }
Outputnya ialah Richard
kerana pengubahsuaian kepada orang dibuat pada salinannya. Jika anda ingin menukar nilai objek orang yang mendasari, anda perlu menggunakan penunjuk.
func main() { p := person{"Richard"} rename(&p) fmt.Println(p) }func rename(p *person) { p.name = "test" }
Seperti di atas, keluarkan test
. Kebolehubahan ialah situasi di mana penunjuk digunakan dalam Go. Sama ada ini adalah perkara yang baik untuk dibahaskan.
Gunakan penunjuk untuk mengekalkan nilai terkini. Ini memastikan API konsisten walaupun tidak semua kaedah mengubah nilainya.
Oleh itu, ini:
func (p *person) rename(s string) { p.name = s }func (p *person) printName() { fmt.Println(p.name) }
lebih baik daripada
func (p *person) rename(s string) { p.name = s }func (p person) printName() { fmt.Println(p.name) }
walaupun tidak perlu menggunakan penunjuk dalam printName
untuk konsistensi. Tetapi ini akan menjadikan API lebih mudah dan mengelak daripada mengingati di mana rujukan diperlukan.
yang hilang, apabila digunakan, mempunyai nilai lalai sifar. Tetapi terdapat senario di mana anda perlu tahu bahawa ada sesuatu yang hilang atau mempunyai nilai yang tidak diisi. Sebagai contoh, struktur mengandungi skor ujian pelajar Jika struktur kosong dan mempunyai skor 0, adakah ia bermakna pelajar itu tidak berjaya dalam ujian, atau tidak mengambil ujian langsung? Nilai sifar lalai untuk penunjuk
ialah penunjuk nil
, yang bermaksud tiada nilai ditetapkan. Keperluan ini juga boleh dilaksanakan seperti berikut:
type exam struct { score int present bool }
Gunakan medan present
yang berasingan untuk menunjukkan pelajar tidak mengambil peperiksaan.
Ini agak subjektif. Orang yang berbeza mempunyai pemahaman yang berbeza tentang pengaturcaraan, jadi kami tidak perlu mempunyai konsep yang sama
Saya percaya adalah masuk akal untuk mempunyai nilai lalai dalam nilai Go sebanyak mungkin. Ini mungkin tidak berfungsi dalam setiap keadaan, tetapi dalam kes saya ia menyelamatkan saya daripada kemalangan besar. Menggunakan nilai dan bukannya penunjuk tidak akan menyebabkan "kesilapan berjuta dolar" Tony Hoare disebabkan oleh penunjuk nol.
Nilai lalai sifar berguna untuk mengelakkan banyak pengisytiharan.
另一个好处是易变性造成的问题比它解决的问题多的得多。易变性给函数带来的副作用同时使得调试变得更加困难。 通过让函数返回修改之后的结构体,可以避免这种突变。
重写之前的例子
func main() { p := person{"richard"} p = rename(p) fmt.Println(p) }func rename(p person) person { p.name = "test" return p }
这也是 append
如何工作的,所以并不陌生。
x := []int{1,2} x = append(x, 3) x = append(x, 4)
鉴于指针的安全性,和值处理比指针处理更快,使用指针需要反复斟酌。
原文地址:https://medium.com/@meeusdylan/when-to-use-pointers-in-go-44c15fe04eac
译文地址:https://learnku.com/go/t/60923
Atas ialah kandungan terperinci Adakah anda tahu bila hendak menggunakan penunjuk Go?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!