Dalam wawancara Slashdot ini, Linus Torvalds dikutip mengatakan:
Saya telah melihat terlalu banyak orang yang menghapus entri daftar yang ditautkan tunggal dengan melacak entri "sebelumnya", dan kemudian menghapus entri, melakukan sesuatu seperti
if (prev)
prev-> next = entry-> next;
lain
list_head = entri-> selanjutnya;dan setiap kali saya melihat kode seperti itu, saya hanya pergi "Orang ini tidak mengerti petunjuk". Dan itu sangat umum.
Orang-orang yang mengerti pointer hanya menggunakan "pointer ke entry pointer", dan menginisialisasi dengan alamat dari list_head. Dan ketika mereka melintasi daftar, mereka dapat menghapus entri tanpa menggunakan kondisional apa pun, dengan hanya melakukan "* pp = entri-> selanjutnya".
Sebagai pengembang PHP saya belum menyentuh pointer sejak Pengantar C di universitas satu dekade lalu. Namun, saya merasa ini adalah jenis situasi yang setidaknya harus saya kenal. Apa yang Linus bicarakan? Sejujurnya, jika saya diminta untuk mengimplementasikan daftar tertaut dan menghapus item, cara 'salah' di atas adalah cara saya melakukannya. Apa yang perlu saya ketahui kode sebagai Linus mengatakan yang terbaik?
Saya bertanya di sini daripada di Stack Overflow karena saya sebenarnya tidak memiliki masalah dengan ini dalam kode produksi.
prev
, alih-alih menyimpan seluruh node, Anda hanya dapat menyimpan lokasiprev.next
, karena itulah satu-satunya hal yang Anda minati. Penunjuk ke penunjuk. Dan jika Anda melakukan itu, Anda menghindari yang konyolif
, karena sekarang Anda tidak memiliki kasus canggunglist_head
menjadi penunjuk dari luar node. Pointer ke kepala daftar kemudian secara semantik sama dengan pointer ke node berikutnya.Jawaban:
Menggunakan keterampilan L331 MS Paint saya:
Solusi asli adalah dengan menunjuk ke Nodes via
curr
. Dalam hal ini Anda memeriksa apakah simpul berikutnya setelahcurr
memiliki nilai hapus, dan jika demikian reset pointercurr
simpulnext
. Masalahnya adalah bahwa tidak ada simpul yang menunjuk ke kepala daftar. Itu berarti harus ada kasus khusus untuk memeriksanya.Apa yang Linus (kemungkinan) usulkan bukan untuk menyimpan pointer ke node yang diperiksa saat ini, melainkan pointer ke pointer ke node saat ini (berlabel
pp
). Operasi ini sama - jikapp
pointer menunjuk ke sebuah simpul dengan nilai yang tepat, Anda meresetpp
pointer tersebut.Perbedaannya ada di bagian paling awal dari daftar. Meskipun tidak ada Node yang menunjuk ke kepala daftar, sebenarnya ada sebuah penunjuk ke kepala daftar. Dan itu adalah pointer sama ke node, sama seperti
next
pointer node lain . Karenanya tidak perlu klausa khusus untuk awal daftar.sumber
list_head
menunjuk ke sesuatu dengannext
simpul yang menunjuk ke item data nyata pertama (dan telahprev
diinisialisasi ke objek boneka yang sama). Saya tidak suka ide untukprev
menunjuk sesuatu yang berbeda, karena trik seperti itu dapat memperkenalkan Perilaku Tidak Terdefinisi melalui aliasing, dan membuat kode sensitif terhadap tata letak struktur.prev
menunjuk ke Nodes, itu menunjuk ke pointer. Selalu menunjuk ke sesuatu dari jenis yang sama, yaitu pointer ke Node. Saran Anda pada dasarnya sama -prev
arahkan ke sesuatu "dengannext
simpul". Jika Anda membuang shell Anda hanya mendapatkanlist_head
pointer awal . Atau dengan kata lain - sesuatu yang didefinisikan hanya dengan memiliki pointer ke node berikutnya, secara semantik setara dengan pointer ke sebuah node.list_head
dannext
akan memegang "jenis" pointer yang sama. Bukan masalah di C, mungkin, tapi mungkin bermasalah di C ++.Contoh Kode
Jika kita membutuhkan logika untuk menghancurkan node, maka kita bisa menambahkan satu baris kode di akhir:
sumber