程式碼除錯技巧【OI縮水版】

小哈里發表於2018-04-17

1 小黃鴨除錯法

來自維基:小黃鴨除錯法是軟體工程中使用的除錯程式碼方法之一。

就是在程式的除錯、糾錯或測試過程中,耐心地向小黃鴨解釋每一行程式的作用,以此來激發靈感。

2 輸出中間值

在關鍵位置輸出值

適用於以下一些:
資料輸入,輸出
死迴圈,盞溢位
過程值,語義分析(較痛苦
much more :

適用於細節手誤沒看到。
操作函式化,分塊測試,能獨立測試的演算法部分先驗證其正確性。
對照神犇程式碼,用一樣的部分替換掉自己程式碼,並測試答案。

3 斷點、單步

GDB除錯技巧

我也不會啊,我也很無奈啊。

4 對拍

大量隨機資料測試,正確性判斷

步驟

  1. 寫好程式和暴力
  2. 寫好資料生成器
  3. 寫好對拍檔案

關於對拍指令碼

”Windows 環境下對拍檔案.bat
“用重定向的方式代替程式碼內的檔案輸入輸出
@echo off "關掉螢幕顯示
:loop "迴圈
  rand.exe %random% > input.txt “隨機生成資料
  test.exe < input.txt > test.out ”執行錯解
  std.exe < input.txt > std.out “執行標程
  fc test.out std.out “比較輸出
  if errorlevel 1 pause ”如果不同就停下來
goto loop “重複迴圈
#Linux 環境下對拍檔案.sh
#!/bin/bash
while true; do
  ./rand > input.txt
  ./test < input.txt > test.out
  ./std < input.txt > std.out
  if diff std.out test.out; then
      printf "AC\n"
  else
      printf "Wa\n"
      exit 0
  fi
done

關於資料生成器

#include<cstdlib>
#include<ctime>
int random(int n){//返回一個[0,n-1]的隨機整數。
    return (long long)rand()*rand()%n;
}
int main(){
    srand((unsigned)time(0));
    return 0;
}

1.隨機生成整數序列

int n = random(100000)+1;
int m = 1000000000;
for(int i = 1; i <= n; i++)
    a[i] = random(2*m+1)-m;

2.隨機生成區間列

for(int i = 1; i <= m; i++){
    int l = random(n)+1;
    int r = random(n)+1;
    if(l > r)swap(l,r);
    cout<<l<<" "<<r<<"\n";
}

3.如何生成一棵樹?

for(int i = 2; i <= n; i++){
    //從2~n之間的每個點i向1~i-1之間的點隨機連一條邊
    int fa = random(i-1)+1;
    int val = random(1000000000)+1;
    cout<<fa<<" "<<i<<" "<<val<<"\n";
}

4.如何生成一張圖?

pair<int, int>e[1000005];//儲存資料
map<pair<int,int>,bool>h;//防止重邊
//先生成一棵樹,保證聯通
for(int i = 1; i < n; i++){
    int fa = random(i)+1;
    e[i] = make_pair(fa,i+1);
    h[e[i]] = h[make_pair(i+1,fa)] = 1;
}
//再生成剩餘的m-n+1條邊
for(int i = n; i <= m; i++){
    int x, y;
    do{
        x = random(n)+1, y = random(n)+1;
    }while(x==y || h[make_pair(x,y)]);
    e[i] = make_pair(x, y);
    h[e[i]] = h[make_pair(y,x)] = 1;
}
//隨機打亂,輸出
cout<<n<<" "<<m<<"\n";
random_shuffle(e+1,e+m+1);
for(int i = 1; i <= m; i++)
    cout<<e[i].first<<" "<<e[i].second<<"\n";

5 靜態查錯

程式按照思路編完之後,查編譯錯誤。

編譯全部修正後,千萬不要測樣例。

1、經驗證明,第一次就把樣例過了的機率很低,即使過了,在測自己的特殊資料的時候也會出錯。所以,編譯完後一定要靜態查錯。

2、經驗表明,靜態查錯是很有效果的。基本上每次靜態查錯都可以找到變數代錯的錯誤。特別是快排的I,J是否帶錯,DEC,INC是否搞錯,SWAP是不是加了VAR等等。

3、試想:如果沒有靜態查錯,就去測樣例,如果程式有錯,樣例不過,影響心情;即使樣例過了,因為程式有錯,特殊資料也不一定能過;即使特殊資料也過了,程式有錯,評測的時候絕對會錯。發現錯了,影響心情了,還是要來靜態查,心情不好,肯定效率低。那還不如一開始就靜態查,即使發現錯誤,獲得成就感,心情很好。千萬不要慌著去測。

4、要保證程式無錯,思路清晰,結構清晰了,然後再去測樣例,再去測特殊資料。
樣例過了不要得意,特殊資料過了不要得意,很有可能還有很多特殊情況你沒有想到。

(1)是否寫上了using namespace std? (這是C++的,Pascal就不用了)
(2)陣列開得是否夠大?
(3)變數型別是否正確?
(4)memset時,所填的sizeof(XX)的XX是不是匹配?大小是不是正確? (Pascal 是 fillchar)
(5)外層迴圈與內層迴圈的i,j是不是混用了?
(6)迴圈之前,i,j是否定義了?
(7)輸入資料都輸入了嗎?
(8)這個程式是在執行你想讓它執行的步驟嗎?

6 其他一些

  1. 重寫程式碼(霧

  2. 拒絕除錯(逃

相關文章