Memperbaiki kesalahan Segmentasi di C ++

95

Saya menulis program C ++ lintas platform untuk Windows dan Unix. Di sisi Window, kode akan mengkompilasi dan mengeksekusi tanpa masalah. Di sisi Unix, itu akan dikompilasi namun ketika saya mencoba menjalankannya, saya mendapatkan kesalahan segmentasi. Firasat awal saya adalah bahwa ada masalah dengan petunjuk.

Apa metodologi yang baik untuk menemukan dan memperbaiki kesalahan kesalahan segmentasi?

Elpezmuerto
sumber

Jawaban:

135
  1. Kompilasi aplikasi Anda dengan -g, maka Anda akan memiliki simbol debug di file biner.

  2. Gunakan gdbuntuk membuka konsol gdb.

  3. Gunakan filedan teruskan file biner aplikasi Anda di konsol.

  4. Gunakan rundan teruskan argumen apa pun yang dibutuhkan aplikasi Anda untuk memulai.

  5. Lakukan sesuatu untuk menyebabkan Kesalahan Segmentasi .

  6. Ketik btdi gdbkonsol untuk mendapatkan setumpuk jejak dari Segmentation Fault .

Svisstack
sumber
Apa yang dimaksud dengan kompilasi gdalam konteks CMake?
Schütze
2
Aktifkan jenis build debug. Salah satunya adalah cmake -DCMAKE_BUILD_TYPE=Debug.
Antonin Décimo
36

Terkadang kecelakaan itu sendiri bukanlah penyebab sebenarnya dari masalah tersebut - mungkin ingatannya hancur pada titik sebelumnya, tetapi perlu beberapa saat agar korupsi muncul dengan sendirinya. Lihat valgrind , yang memiliki banyak pemeriksaan untuk masalah penunjuk (termasuk pemeriksaan batas array). Ini akan memberi tahu Anda di mana masalahnya dimulai , bukan hanya baris tempat terjadinya crash.

paleozogt.dll
sumber
19

Sebelum masalah muncul, cobalah untuk menghindarinya sebanyak mungkin:

  • Kompilasi dan jalankan kode Anda sesering mungkin. Akan lebih mudah untuk menemukan bagian yang salah.
  • Cobalah untuk merangkum rutinitas tingkat rendah / rawan kesalahan sehingga Anda jarang harus bekerja langsung dengan memori (perhatikan pemodelan program Anda)
  • Pertahankan rangkaian pengujian. Memiliki gambaran umum tentang apa yang saat ini berfungsi, apa yang tidak lagi berfungsi, dll, akan membantu Anda untuk mengetahui di mana masalahnya ( Tes peningkatan adalah solusi yang mungkin, saya tidak menggunakannya sendiri tetapi dokumentasi dapat membantu untuk memahami jenis apa informasi harus ditampilkan).

Gunakan alat yang sesuai untuk debugging. Di Unix:

  • GDB dapat memberi tahu Anda di mana Anda memprogram crash dan akan membiarkan Anda melihat dalam konteks apa.
  • Valgrind akan membantu Anda mendeteksi banyak kesalahan terkait memori.
  • Dengan GCC Anda juga dapat menggunakan mudflap Dengan GCC, Clang dan sejak Oktober percobaan MSVC Anda dapat menggunakan Address / Memory Sanitizer . Itu dapat mendeteksi beberapa kesalahan yang tidak dilakukan Valgrind dan kehilangan kinerja lebih ringan. Ini digunakan dengan mengkompilasi dengan -fsanitize=addressbendera.

Akhirnya saya akan merekomendasikan hal-hal biasa. Semakin banyak program Anda dapat dibaca, dipelihara, jelas dan rapi, semakin mudah untuk melakukan debug.

log0
sumber
5

Di Unix, Anda dapat menggunakan valgrinduntuk menemukan masalah. Gratis dan bertenaga. Jika Anda lebih suka melakukannya sendiri, Anda dapat membebani operator newdan deleteuntuk menyiapkan konfigurasi di mana Anda memiliki 1 byte dengan 0xDEADBEEFsebelum dan sesudah setiap objek baru. Kemudian lacak apa yang terjadi di setiap iterasi. Ini bisa gagal untuk menangkap semuanya (Anda bahkan tidak dijamin untuk menyentuh byte tersebut) tetapi ini telah berhasil untuk saya di masa lalu pada platform Windows.

wheaties
sumber
1
baik ini akan menjadi 4 byte daripada 1 ... tetapi prinsipnya baik-baik saja.
Jonas Wagner
1
Bolehkah saya menautkan ke debugger heap non-intrusif saya ? :-)
fredoverflow
Lakukanlah. Kami semua tentang membantu orang lain di sini jadi apa pun yang dapat membantu harus ditambahkan.
Wheaties
Meskipun kelebihan beban newdan deletedapat sangat berguna, penggunaan -fsanitize=addressadalah pilihan yang lebih baik karena kompilator akan mengkompilasi dalam deteksi runtime untuk masalah dan akan membuang memori secara otomatis ke layar yang membuat proses debug lebih mudah.
Tarick Welling
3

Ya, ada masalah dengan pointer. Sangat mungkin Anda menggunakan salah satu yang tidak diinisialisasi dengan benar, tetapi mungkin juga Anda mengacaukan manajemen memori Anda dengan kebebasan ganda atau semacamnya.

Untuk menghindari pointer yang tidak diinisialisasi sebagai variabel lokal, coba deklarasikannya selambat mungkin, sebaiknya (dan ini tidak selalu memungkinkan) ketika mereka dapat diinisialisasi dengan nilai yang berarti. Yakinkan diri Anda sendiri bahwa mereka akan memiliki nilai sebelum digunakan, dengan memeriksa kodenya. Jika Anda mengalami kesulitan dengan itu, inisialisasi mereka ke konstanta penunjuk nol (biasanya ditulis sebagai NULLatau0 ) dan periksa.

Untuk menghindari pointer yang tidak diinisialisasi sebagai nilai anggota, pastikan mereka diinisialisasi dengan benar di konstruktor, dan ditangani dengan benar di konstruktor salinan dan operator penugasan. Jangan mengandalkan initfungsi untuk manajemen memori, meskipun Anda bisa untuk inisialisasi lainnya.

Jika kelas Anda tidak memerlukan konstruktor salinan atau operator tugas, Anda dapat mendeklarasikannya sebagai fungsi anggota pribadi dan tidak pernah mendefinisikannya. Itu akan menyebabkan kesalahan kompilator jika mereka digunakan secara eksplisit atau implisit.

Gunakan petunjuk cerdas jika memungkinkan. Keuntungan besar di sini adalah, jika Anda tetap menggunakannya dan menggunakannya secara konsisten, Anda benar-benar dapat menghindari penulisan deletedan tidak ada yang akan terhapus ganda.

Gunakan string C ++ dan kelas container jika memungkinkan, sebagai ganti string dan array gaya C. Pertimbangkan untuk menggunakan .at(i)daripada [i], karena itu akan memaksa pemeriksaan batas. Lihat apakah compiler atau library Anda dapat disetel untuk memeriksa batasan [i], setidaknya dalam mode debug. Kesalahan segmentasi dapat disebabkan oleh buffer overruns yang menulis sampah di atas pointer yang sangat bagus.

Melakukan hal-hal tersebut akan sangat mengurangi kemungkinan kesalahan segmentasi dan masalah memori lainnya. Mereka pasti akan gagal untuk memperbaiki semuanya, dan itulah mengapa Anda harus menggunakan valgrind sekarang dan nanti ketika Anda tidak memiliki masalah, dan valgrind dan gdb ketika Anda melakukannya.

David Thornley
sumber
1

Saya tidak tahu metodologi apa pun yang digunakan untuk memperbaiki hal-hal seperti ini. Saya tidak berpikir itu akan mungkin untuk muncul dengan salah satu karena masalah yang dihadapi adalah bahwa perilaku program Anda tidak terdefinisi (Saya tidak tahu kasus apa pun ketika SEGFAULT tidak disebabkan oleh semacam UB) .

Ada berbagai macam "metodologi" untuk menghindari masalah sebelum masalah itu muncul. Salah satu yang penting adalah RAII.

Selain itu, Anda hanya perlu membuang energi psikis terbaik Anda padanya.

Edward Strange
sumber