2010-09-15 82 views
46

我正在爲Windows和Unix編寫一個跨平臺的C++程序。在窗口一側,代碼將編譯並執行沒有問題。在Unix端,它會編譯,但是當我嘗試運行它時,我得到了分段錯誤。我最初的直覺是指針有問題。修復C++中的分割錯誤

找到並修復分段故障錯誤的好方法是什麼?

回答

71
  1. -g編譯應用程序,那麼你就必須調試符號二進制文件。使用gdb打開gdb控制檯。

  2. 使用file並將其應用程序的二進制文件傳遞到控制檯。

  3. 使用run並傳入您的應用程序需要啓動的任何參數。

  4. 做些什麼導致分割錯誤

  5. gdb控制檯類型bt得到分段錯誤的堆棧跟蹤。

2

在Unix上,您可以使用valgrind來查找問題。它免費且強大。如果您寧願自己動手,您可以重載新操作符和刪除操作符,以在每個新對象之前和之後設置一個配置,其中您有1個字節,其中包含0xDEADBEEF。然後跟蹤每次迭代發生的情況。這可能無法捕捉到所有內容(您甚至不能保證觸及這些字節),但它在Windows平臺上過去對我來說很有用。

+1

以及這將是4個字節,而不是1 ...但原則是好的。 – 2010-09-15 15:57:36

+0

我可以鏈接到我的[非侵入式堆調試器](http://stackoverflow.com/questions/2835416)嗎? :-) – fredoverflow 2010-09-15 16:31:08

+0

去吧。我們都在幫助別人,所以任何可以幫助的東西都應該加上。 – wheaties 2010-09-15 17:03:00

22

有時,崩潰本身並不是問題的真正原因 - 也許內存在早期被粉碎,但腐敗顯現出來需要一段時間。檢查出valgrind,它有很多指針問題檢查(包括數組邊界檢查)。它會告訴你問題從哪裏開始,而不僅僅是發生崩潰的線路。

14

問題出現之前,儘量避免它儘可能:

  • 編譯和經常可以運行代碼。找到故障部分將更容易。
  • 嘗試封裝低級別/容易出錯的例程,以便您很少必須直接使用內存(請注意程序的型號化)
  • 維護一個測試套件。總結一下當前的工作情況,沒有更多的工作等等,將幫助你找出問題所在(Boost test是一個可能的解決方案,我不是自己使用它,但文檔可以幫助理解什麼樣的信息必須顯示)。

使用適當的工具進行調試。在Unix上:

  • GDB可以告訴你你程序崩潰的地方,並會讓你看到在什麼情況下。
  • Valgrind將幫助您檢測許多與內存相關的錯誤。
  • 使用GCC,您還可以使用mudflap使用GCC和Clang,您可以使用Address/Memory Sanitizer。它可以檢測Valgrind沒有的錯誤,並且性能損失更輕。

最後我會推薦平時的事情。程序可讀性越高,可維護性越強,清晰整潔,最容易進行調試。

0

我不知道任何方法來解決這樣的事情。我認爲不可能爲手頭的問題提出一個問題,那就是你的程序的行爲是不確定的(我不知道SEGFAULT並不是由某種UB引起的) 。

有各種各樣的「方法論」來避免問題發生。一個重要的是RAII。

除此之外,你只需要投入你最好的精神能量。

2

是的,指針有問題。很可能你正在使用一個沒有正確初始化的程序,但也有可能你用雙重空閒或其他方式搞亂了你的內存管理。

爲了避免未初始化的指針作爲局部變量,儘可能遲的嘗試聲明它們,最好(並不總是可能的),當它們可以用有意義的值初始化時。通過檢查代碼,說服他們在使用之前會有價值。如果您遇到困難,請將它們初始化爲空指針常量(通常寫爲NULL0)並檢查它們。

要避免未初始化的指針作爲成員值,請確保它們已在構造函數中正確初始化,並在複製構造函數和賦值運算符中正確處理。雖然可以進行其他初始化,但不要依賴內存管理的init函數。

如果你的類不需要拷貝構造函數或賦值操作符,你可以將它們聲明爲私有成員函數,並且永遠不要定義它們。這會導致編譯器錯誤,如果他們明確或隱式地使用。

適用時使用智能指針。這裏最大的好處是,如果你堅持使用它們並且一致地使用它們,你就完全可以避免寫入delete,而且沒有東西會被雙重刪除。

儘可能使用C++字符串和容器類,而不是C風格的字符串和數組。考慮使用.at(i)而不是[i],因爲這會強制邊界檢查。至少在調試模式下,看看您的編譯器或庫是否可以設置爲檢查[i]上的界限。分段錯誤可能由緩衝區溢出引起,這些緩衝區溢出將垃圾寫入完美的指針。

做這些事情將大大降低分段錯誤和其他內存問題的可能性。他們無疑將無法解決所有問題,這就是爲什麼你應該不時地使用valgrind,而當你沒有問題的時候,以及valgrind和gdb。