差不多每次編一些競賽類的程序都會至少給我報一次runtime_error(運行時錯誤)。這或許也是廣大OIer心中永遠的痛。~_~
本文主要討論如何對runtime_error以及其中的segment_fault部分進行調試。本文以dev-c++5.11作為環境,由於以競賽為主,本文不涉及指針、c++中的類等部分。
下面所有經驗,均為本人在編程過程中所實際涉及並進行應用。
一、關於runtime_error(運行時錯誤)
運行時錯誤大概分為以下幾類:
1、訪問到不應訪問的地址(如數組越界)
2、內存溢出(如數組過大)
3、算術上溢或下溢(如除以0)
4、段錯誤(各種其它錯誤)
除最后一種情況的一部分外,剩余錯誤發生時通常會在執行.exe文件時顯示“已停止運行”。
(一)地址越界及溢出
通常情況下(不考慮指針地址非法)數組開過大會導致編譯錯誤(Complie Error)。但是訪問下標負數或數組過小會導致訪問到數組以外的范圍。
除將數組范圍增大之外,通常情況下可以開一個新數組,當數組內存溢出時多余內容會流入新數組以避免錯誤發生(不推薦這種做法,無法確定內容剩余部分)。
(二)算術上溢及下溢
我們可以在每個可能出現錯誤的位置嘗試c++中的try-throw-catch子句。
比如遇到除以0:
#include <iostream> using std::cout; using std::cin; using std::cerr; int fun(int &a, int &b) { if(b == 0) { throw "hello there have zero sorry\n"; //引發異常 } return a / b; } int main() { int a; int b; while(true) { cin >> a; cin >> b; try //try里面是可能引發異常代碼塊 { cout << " a / b = "<< fun(a,b) << "\n"; } catch(const char *str) //接收異常,處理異常 括號內是類型 { cout << str; cerr <<"除數為0\n"; //cerr不會到輸出緩沖中 這樣在緊急情況下也可以使用 }
catch(...)//可以捕捉任何類型的異常
{
cerr <<"未知錯誤\n";
} } system("pause"); return 1;//異常結束 }
try:try塊標識符其中特定的異常可能被激活的代碼塊,他后面跟一個或者多個catch塊.
catch:類似於函數定義,但並不是函數定義,關鍵字catch表明這是給一個處理程序, 里面的const cahr *str 會接受throw傳過來錯誤信息.
throw:拋出異常信息,類似於執行返回語句,因為它將終止函數的執行,但是它不是將控制權交給調用程序,而是導致程序沿着函數調用序列后退,知道找到包含try塊的函數.
(三)對segment_fault的處理
下面是來自Answers.com的定義:
| A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. In short, a segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors. |
另外,這里有個基本上對照的中文解釋,來自http://www.linux999.org/html_sql/3/132559.htm
| 所謂的段錯誤 就是指訪問的內存超出了系統所給這個程序的內存空間,通常這個值是由gdtr來保存的,他是一個48位的寄存器,其中的32位是保存由它指向的gdt表, 后13位保存相應於gdt的下標,最后3位包括了程序是否在內存中以及程序的在cpu中的運行級別,指向的gdt是由以64位為一個單位的表,在這張表中 就保存着程序運行的代碼段以及數據段的起始地址以及與此相應的段限和頁面交換還有程序運行級別還有內存粒度等等的信息。一旦一個程序發生了越界訪 問,cpu就會產生相應的異常保護,於是segmentation fault就出現了 |
實際上同前面一樣,段錯誤也是訪問到了非法的內存,它會拋出SIGSEGV信號,可以通過signal或者dev-c++自帶調試器捕捉。
比如說:

其中scanf調用了a而不是&a
這時我們可以借助c++中的assert函數捕捉異常
首先我們初步判斷這里出現了段錯誤,借助assert的異常判斷我們可以看出是否出現錯誤

在判斷確實出現錯誤以后我們可以借助調試器觀察錯誤位置

經過觀察,我們確定錯誤位於藍色行的"%d",a處
改正就可以了
段錯誤是算法競賽中很容易出現的一種錯誤,我們應該學會使用各種方法盡快加以解決。
