Sửa lỗi Phân đoạn trong C ++


95

Tôi đang viết một chương trình C ++ đa nền tảng cho Windows và Unix. Về phía Window, mã sẽ biên dịch và thực thi không có vấn đề gì. Về phía Unix, nó sẽ biên dịch nhưng khi tôi cố gắng chạy nó, tôi gặp lỗi phân đoạn. Linh cảm ban đầu của tôi là có vấn đề với con trỏ.

Các phương pháp tốt để tìm và sửa lỗi lỗi phân đoạn là gì?

Câu trả lời:


134
  1. Biên dịch ứng dụng của bạn với -g, sau đó bạn sẽ có các ký hiệu gỡ lỗi trong tệp nhị phân.

  2. Sử dụng gdbđể mở bảng điều khiển gdb.

  3. Sử dụng filevà chuyển nó vào tệp nhị phân của ứng dụng của bạn trong bảng điều khiển.

  4. Sử dụng runvà chuyển bất kỳ đối số nào mà ứng dụng của bạn cần để bắt đầu.

  5. Làm điều gì đó để gây ra Lỗi phân đoạn .

  6. bttrong gdbgiao diện điều khiển để có được một stack trace của Segmentation Fault .


Nó có nghĩa là gì khi nó được biên dịch gtrong bối cảnh CMake?
Schütze

2
Bật kiểu xây dựng gỡ lỗi. Một cách là cmake -DCMAKE_BUILD_TYPE=Debug.
Antonin Décimo

36

Đôi khi bản thân sự cố không phải là nguyên nhân thực sự của vấn đề - có lẽ bộ nhớ đã bị phá vỡ ở một thời điểm trước đó nhưng phải mất một thời gian để sự cố mới xuất hiện. Kiểm tra valgrind , có rất nhiều kiểm tra cho các vấn đề con trỏ (bao gồm cả kiểm tra giới hạn mảng). Nó sẽ cho bạn biết vấn đề bắt đầu từ đâu , không chỉ dòng nơi xảy ra sự cố.


19

Trước khi vấn đề phát sinh, hãy cố gắng tránh nó càng nhiều càng tốt:

  • Biên dịch và chạy mã của bạn thường xuyên nếu bạn có thể. Nó sẽ dễ dàng hơn để xác định vị trí phần bị lỗi.
  • Cố gắng đóng gói các quy trình mức thấp / dễ xảy ra lỗi để bạn hiếm khi phải làm việc trực tiếp với bộ nhớ (chú ý đến mô hình hóa chương trình của bạn)
  • Duy trì một bộ thử nghiệm. Có một cái nhìn tổng quan về những gì hiện đang hoạt động, những gì không hoạt động nữa, v.v., sẽ giúp bạn tìm ra vấn đề ở đâu ( Kiểm tra tăng cường là một giải pháp khả thi, tôi không sử dụng bản thân mình nhưng tài liệu có thể giúp bạn hiểu được loại nào thông tin phải được hiển thị).

Sử dụng các công cụ thích hợp để gỡ lỗi. Trên Unix:

  • GDB có thể cho bạn biết chương trình của bạn bị lỗi ở đâu và sẽ cho bạn biết trong bối cảnh nào.
  • Valgrind sẽ giúp bạn phát hiện nhiều lỗi liên quan đến bộ nhớ.
  • Với GCC, bạn cũng có thể sử dụng bùn Với GCC, Clang và kể từ tháng 10 thử nghiệm với MSVC, bạn có thể sử dụng Address / Memory Sanitizer . Nó có thể phát hiện một số lỗi mà Valgrind không có và việc mất hiệu suất nhẹ hơn. Nó được sử dụng bằng cách biên dịch với -fsanitize=addresscờ.

Cuối cùng tôi muốn giới thiệu những điều thông thường. Chương trình của bạn càng dễ đọc, dễ bảo trì, rõ ràng và gọn gàng, thì việc gỡ lỗi càng dễ dàng hơn.


5

Trên Unix, bạn có thể sử dụng valgrindđể tìm các vấn đề. Nó miễn phí và mạnh mẽ. Nếu bạn muốn tự mình làm điều đó, bạn có thể nạp chồng toán tử newand deleteđể thiết lập cấu hình trong đó bạn có 1 byte với 0xDEADBEEFtrước và sau mỗi đối tượng mới. Sau đó, theo dõi những gì xảy ra ở mỗi lần lặp lại. Điều này có thể không bắt được mọi thứ (bạn không được đảm bảo thậm chí chạm vào những byte đó) nhưng nó đã hoạt động với tôi trong quá khứ trên nền tảng Windows.


1
tốt đây sẽ là 4 byte thay vì 1 ... nhưng nguyên tắc là tốt.
Jonas Wagner

1
Tôi có thể liên kết đến trình gỡ lỗi heap không xâm nhập của mình không ? :-)
fredoverflow

Cứ liều thử đi. Tất cả chúng tôi đều muốn giúp đỡ những người khác ở đây nên bất kỳ điều gì có thể giúp được đều nên được thêm vào.
wheaties

Mặc dù quá tải newdeletecó thể cực kỳ hữu ích, nhưng sử dụng -fsanitize=addresslà một lựa chọn tốt hơn vì trình biên dịch sẽ biên dịch trong thời gian chạy phát hiện các sự cố và sẽ tự động kết xuất bộ nhớ ra màn hình, giúp cách gỡ lỗi dễ dàng hơn.
Tarick Welling

3

Có, có vấn đề với con trỏ. Rất có thể bạn đang sử dụng một thiết bị không được khởi tạo đúng cách, nhưng cũng có thể bạn đang làm rối tung việc quản lý bộ nhớ của mình với giải phóng kép hoặc một số thứ như vậy.

Để tránh con trỏ chưa được khởi tạo dưới dạng biến cục bộ, hãy thử khai báo chúng càng muộn càng tốt, tốt nhất là (và điều này không phải lúc nào cũng có thể thực hiện được) khi chúng có thể được khởi tạo với một giá trị có nghĩa. Thuyết phục bản thân rằng chúng sẽ có giá trị trước khi được sử dụng, bằng cách kiểm tra mã. Nếu bạn gặp khó khăn với điều đó, hãy khởi tạo chúng thành hằng số con trỏ null (thường được viết là NULLhoặc 0) và kiểm tra chúng.

Để tránh các con trỏ chưa được khởi tạo dưới dạng giá trị thành viên, hãy đảm bảo rằng chúng được khởi tạo đúng cách trong hàm tạo và được xử lý đúng cách trong các hàm tạo sao chép và toán tử gán. Đừng dựa vào một initchức năng để quản lý bộ nhớ, mặc dù bạn có thể khởi tạo khác.

Nếu lớp của bạn không cần các hàm tạo bản sao hoặc toán tử gán, bạn có thể khai báo chúng dưới dạng các hàm thành viên riêng và không bao giờ định nghĩa chúng. Điều đó sẽ gây ra lỗi trình biên dịch nếu chúng được sử dụng rõ ràng hoặc ngầm định.

Sử dụng con trỏ thông minh khi có thể. Lợi thế lớn ở đây là, nếu bạn kiên trì và sử dụng chúng một cách nhất quán, bạn hoàn toàn có thể tránh viết deletevà không có gì bị xóa hai lần.

Sử dụng chuỗi C ++ và các lớp vùng chứa bất cứ khi nào có thể, thay vì các chuỗi và mảng kiểu C. Cân nhắc sử dụng .at(i)thay vì sử dụng [i], bởi vì điều đó sẽ buộc kiểm tra giới hạn. Xem liệu trình biên dịch hoặc thư viện của bạn có thể được đặt để kiểm tra giới hạn hay không [i], ít nhất là trong chế độ gỡ lỗi. Các lỗi phân đoạn có thể được gây ra bởi các lần ghi đè bộ đệm ghi rác lên các con trỏ hoàn toàn tốt.

Làm những điều đó sẽ làm giảm đáng kể khả năng xảy ra lỗi phân đoạn và các vấn đề về bộ nhớ khác. Chắc chắn họ sẽ không thể sửa chữa mọi thứ, và đó là lý do tại sao bạn nên sử dụng valgrind ngay bây giờ và sau đó khi bạn không gặp vấn đề, và valgrind và gdb khi bạn làm vậy.


1

Tôi không biết bất kỳ phương pháp luận nào để sử dụng để sửa những thứ như thế này. Tôi không nghĩ có thể đưa ra một giải pháp vì vấn đề hiện tại là hành vi của chương trình của bạn là không xác định (Tôi không biết có trường hợp nào khi SEGFAULT không phải do UB nào đó gây ra) .

Có tất cả các loại "phương pháp luận" để tránh vấn đề trước khi nó phát sinh. Một trong những điều quan trọng là RAII.

Bên cạnh đó, bạn chỉ cần ném năng lượng tâm linh tốt nhất của bạn vào nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.