Pengertian Pointer ke Pointer di C

0
11

Kedengarannya seperti mimpi buruk rekursi, tetapi pointer ke pointer adalah pokok pemrograman sistem. Anda pada dasarnya menunjuk pada alamat memori yang menyimpan alamat data Anda yang sebenarnya. Tipuan berlapis ganda inilah yang oleh pengembang disebut sebagai pegangan. Ini bukan hanya trik pengkodean. Ini adalah mekanisme yang diperlukan bagi sistem operasi untuk mengelola memori heap secara efisien.

Anggaplah heap sebagai ruangan yang penuh sesak. Terkadang OS perlu mengacak orang untuk memberi ruang. Jika Anda mengarahkan penunjuk langsung ke seseorang, mereka tidak dapat bergerak tanpa merusak referensi Anda. Namun jika Anda menahan penunjuk ke daftar nama, OS dapat mengubah lokasi siapa pun dalam daftar itu tanpa Anda perlu memperbarui referensi awal Anda. Itulah kekuatan sebuah pegangan.

Berikut adalah bagaimana logika tersebut diterjemahkan ke dalam kode C mentah. Anda mendeklarasikan p sebagai penunjuk ke penunjuk. Kemudian q menjadi penunjuk standar. Anda mengalokasikan memori untuk p, lalu mengalokasikan memori untuk apa yang p tunjuk. Terakhir, Anda melakukan dereferensi dua kali untuk menetapkan nilai 12.

Windows dan macOS mengandalkan struktur ini untuk pemadatan memori. Perbedaan di sini sangatlah penting. Anda, pemrogram, mengelola penunjuk luar p. Sistem operasi mengelola penunjuk bagian dalam *p. Karena OS mengontrol *p, ia dapat merelokasi blok data sebenarnya (**p) ke mana pun di heap. Itu hanya memperbarui *p untuk mencerminkan alamat baru. Kode Anda tetap menggunakan p tanpa hambatan.

Selain manajemen memori OS, pola ini penting untuk meneruskan pointer ke fungsi. Jika Anda memerlukan fungsi untuk memodifikasi pointer itu sendiri, Anda meneruskan pointer ke pointer itu. Ini adalah satu-satunya cara untuk menetapkan ulang referensi dari dalam cakupan yang berbeda.

Mengelola Pointer ke Struktur

Kompleksitasnya tidak berhenti pada bilangan bulat sederhana. Anda dapat menyarangkan logika ini di dalam struktur. Hal ini biasa terjadi saat menangani data dengan panjang variabel seperti string.

Ambil struktur Addr. Ini menampung array berukuran tetap untuk nama, kota, dan telepon. Namun komentarnya berbeda-beda panjangnya. Jadi komentar didefinisikan sebagai penunjuk ke char. Saat Anda mengalokasikan memori untuk struktur itu sendiri, Anda menyediakan ruang untuk pointer dan array tetap. Anda belum menyediakan ruang untuk teks komentar sebenarnya.

Anda mengalokasikan s terlebih dahulu. Kemudian Anda membaca input pengguna ke buffer tetap. Setelah itu, Anda membaca komentar tersebut untuk sementara

Anda kehilangan memori ketika Anda mengabaikan bagaimana pointer bersarang.

Ambil pointer s yang menunjuk ke suatu struktur. Struktur itu menyimpan petunjuk lain. Penunjuk kedua menunjuk ke string sebenarnya di memori. Dua lapisan tipuan. Satu alokasi untuk struct. Satu alokasi untuk string.

Cukup sederhana sampai Anda mencoba membersihkannya.

Di sinilah sebagian besar pengembang melakukan perjalanan. Anda melihat gratis. Anda pikir Anda sudah selesai. Anda salah.

Lihat cuplikan kode ini:

Variabel s menunjuk ke struktur Addr. Di dalam struktur itu, ada kolom komentar. comment menunjuk ke blok memori heap yang dialokasikan untuk data string.

Saat Anda memanggil free(s), Anda melepaskan memori untuk struktur itu sendiri. Struktur Addr hilang. Memori dikembalikan ke sistem.

Tapi apa yang terjadi pada s->komentar?

Ia menghilang ke dalam eter.

Penunjuk ke data string disimpan di dalam struktur yang baru saja Anda bebaskan. Anda tidak dapat mengaksesnya lagi. Anda tidak memanggil free() pada string. Memori tetap dialokasikan tetapi tidak dapat dijangkau.

Ini adalah kebocoran memori.

Ini bukan kecelakaan. Ini bukan pesan kesalahan. Program berjalan dengan baik. Ini perlahan-lahan menghabiskan lebih banyak RAM hingga sistem bertukar atau mogok.

Cara memperbaiki kebocoran penunjuk ganda

Anda harus mengosongkan penunjuk bagian dalam terlebih dahulu. Atau simpan dalam variabel sementara sebelum membebaskan struktur luar.

Sekarang data string dirilis. Kemudian strukturnya dilepaskan. Tidak ada blok yang hilang.

Mengapa hal ini sering terjadi

Orang-orang memperlakukan petunjuk sebagai nilai. Mereka lupa bahwa petunjuk adalah referensi ke sumber daya.

Ketika suatu struktur berisi sebuah pointer, struktur tersebut tidak berdiri sendiri. Itu bergantung pada memori eksternal. Membebaskan wadah tidak berarti membebaskan isinya.

Hal ini menjadi lebih buruk jika sarangnya lebih dalam. Sebuah penunjuk ke sebuah penunjuk ke sebuah penunjuk. Tiga tingkat. Anda memerlukan tiga panggilan gratis(). Dalam urutan yang benar.

Jika Anda lupa satu, Anda bocor.

Masalah get()

Contohnya menggunakan gets(). Fungsi itu berbahaya. Ini telah dihapus dari standar C11 karena memungkinkan buffer overflow. Namun logika memorinya tetap sama.

Baik Anda menggunakan gets(), fgets(), atau scanf(), strategi alokasi merupakan penghambat kebocoran di sini.

Anda mengalokasikan untuk s.
Anda mengalokasikan untuk s->komentar.

Jika Anda hanya mengosongkan s, tinggalkan s->komentar.

Poin penting

  • Pointer ganda memerlukan kebebasan ganda.
  • Periksa setiap malloc untuk gratis yang sesuai.
  • Jika suatu struktur menyimpan penunjuk ke memori dinamis, memori tersebut harus dibebaskan sebelum struktur.
  • free() tidak membebaskan anggota secara rekursif.

Sangat mudah untuk dilewatkan. Kode dikompilasi. Ini berjalan. Kebocorannya tidak bersuara.

Sampai tidak.

Membangun Daftar Tertaut

Anda dapat mengalami bencana memori jika Anda membuang kontainer sebelum data yang dituju. Struktur yang menahan penunjuk dibersihkan. Blok string tetap ada. Itu menjadi blok yang hilang. Hal ini terjadi jika urutan pembuangannya salah.

Menghubungkan

Struktur dapat menunjuk pada dirinya sendiri. Hal ini memungkinkan Anda untuk menyatukan catatan yang identik. Hasilnya adalah daftar tertaut. Ini adalah cara standar untuk mengatur data dalam C.

Inilah cara Anda mendefinisikannya:

typedef struct { nama karakter[21]; kota char[21]; status karakter[21]; Tambahkan berikutnya; } Tambahan;
Tambahkan
pertama;

Bidang berikutnya menyimpan alamat rekaman berikutnya. Anda menggunakan variabel penunjuk tunggal untuk memulai rantai.

Biaya Fleksibilitas

Kompiler memungkinkan Anda membengkokkan aturan yang mungkin tampak berlawanan dengan intuisi pada pandangan pertama. Dengan pengalaman yang cukup, Anda dapat merekayasa struktur seperti yang ditunjukkan di atas. Ini adalah langkah yang kuat.

Namun hal ini bukannya tanpa risiko. Anda sedang melewati garis tipis antara kode pintar dan spageti yang tidak bisa dipelihara.

Mengapa Itu Penting

Ini bukan hanya tentang sintaksis. Ini tentang apa yang bahasa tersebut izinkan untuk Anda lakukan saat Anda mengatasi batasannya.

  • Kontrol : Anda mendapatkan kontrol terperinci atas tata letak memori.
  • Interoperabilitas : Anda dapat berkomunikasi dengan perpustakaan C dengan lebih mudah.
  • Kinerja : Terkadang, melewati pemeriksaan keselamatan akan menghemat siklus.

Tangkapannya? Kompiler tidak akan menyelamatkan Anda dari diri Anda sendiri. Ini akan memungkinkan Anda mengkompilasi kode yang crash saat runtime.

Cara Menggunakannya dengan Aman

Jika Anda ingin melakukan ini, lakukanlah dengan niat.

  1. Isolasi : Jangan sebarkan pola ini ke seluruh basis kode Anda. Simpan dalam modul kecil yang telah teruji dengan baik.
  2. Dokumentasikan semuanya : Di masa depan Anda akan berterima kasih kepada Anda saat ini. Atau membencimu. Mungkin membencimu.
  3. Gunakan abstraksi : Bungkus pointer mentah atau blok tidak aman dalam antarmuka yang bersih. Sembunyikan kekacauan itu.

Pemeriksaan Realitas

Kebanyakan pengembang tidak memerlukan ini. Mereka akan menggunakan struktur standar. Mereka akan lebih bahagia karenanya.

Namun ketika Anda menemui hambatan di mana perpustakaan standar tidak sesuai, Anda akan senang kompiler tidak menghentikan Anda.

Pertanyaannya adalah apakah Anda siap membayar biaya pemeliharaan.

Ini adalah trade-off. Kecepatan untuk keselamatan. Kekuatan untuk kejelasan.

Anda memilih.

попередня статтяUnix vs Linux: Mengapa Perbedaan Penting bagi Pengembang
наступна статтяBagaimana Synthesizer Mengubah Musik dengan Melanggar Aturan Bentuk