標籤:

oi里有什麼優秀的調試技巧?


唔...猶記候選隊胡策的第一場...某鋼琴大佬出了一道碼農題..

我和噸老師和假老師連麥做胡策..說是開黑..結果變成了3個人懟3道題(選對題的我最後是唯一一個A掉題的人)

那場比賽讓我發現了一個極其優秀的調試方法..

那就是..

盡量拒絕調試..

高三之後..由於高二的時候因為調試丟過很多分..於是個人喜歡在平時做題的時候..不把所有細節想完不動手。這些細節甚至細化到類似於這個函數我要用多少個臨時變數..這樣可以最大限度的減少你出錯的概率。然後為了防止手誤,我習慣把不同的變數名盡量區分開,比如用了N就不會用NN, N2這種東西。 同時盡量用大寫變數名..因為小寫的時候容易一下就晃過去了..大寫的話你會習慣性確認一遍是不是大寫了...然後有手誤的話就會發現..

到退役前我經常是行雲流水寫完代碼改完編譯錯誤(基本上是沒看到的手誤, 體現了區分變數名的優越性)..自信sub返回AC..(雖然退役前的最後一道題就這麼咖喱給給了)

比如胡策的時候那道碼農題

我從看完題到寫完它花了兩個多小時, 期間噸老師和假老師聽見我不斷在吐槽這道題有多難寫..結果編譯通過之後就直接AC了..

總之我覺得人不能依賴調試, 調試是你效率最低的時候..在比賽上越調你就會越慌張..與其期待調試技巧, 不如盡量避免調試, 提高一次寫對的概率..這件事情在oi裡面其實是不難的, 只要平時寫題養成好的習慣...就絕對可以做到..

以上


蒟蒻來強答一發。

我考場上習慣先寫暴力,然後寫正解,用批處理掛起來對拍。這時候可以出去上個廁所,回來的時候對拍沒掛就基本上對了。

(首先得保證暴力不掛。。。)

寫正解的時候,可以寫一部分調一部分,而不是整個程序寫完再一起調。

比如寫一個樹剖,先把線段樹的部分寫好,自己出一些數據輸進去測試一下。然後再寫樹上的部分,最後一起調試。

親測這樣可以避免很多SB錯誤,比如變數名打錯,循環打反之類的。


update2017.06.07

認真說兩句。

非常認同 @YJQ 的觀點。最好的debug技巧就是減少bug。

簡單概括一下我的想法。

有很多人把演算法框架設計歸為演算法設計,而把細節全部歸為實現,個人認為這在演算法競賽中是十分糟糕的。如果你這麼做,那麼你在實現的過程中,就會有非常非常多、雜的細節需要去考慮,最終導致你難免會有考慮不到的地方。

建議把演算法的細節歸入演算法設計。當你的演算法設計涵蓋了幾乎所有的演算法上的細節、以及足夠多的純實現上的細節,你在寫代碼的時候就有更多的精力用來放在你的程序是否嚴格地實現了你的演算法上,在這樣的情況下,如果你再有意識地去顧及這些,那麼你的bug就很自然地變少了。

我也說個我自己的故事吧。我高二的時候大部分時間學校是不讓停課的,然而我又並不喜歡在手機上寫代碼,於是我就選擇了在紙上寫代碼(每天進教室前記幾個題,然後上課的時候想寫,放學之後敲debug)。可能更多的大佬們會有在手機上寫代碼的經歷。但我覺得在紙上寫題給我的幫助是另一方面的,因為在紙上寫字是不能backspace的,於是它就強迫我想清楚框架再開始動筆、想清楚所有東西再寫每一句話(當然,這也是因為我有一點強迫症,這個方法對那些【覺得紙上塗塗改改一堆都無所謂的人】可能並沒有效果……)。

其實,如果你仔細撕烤,會發現我在分鴿線上和分鴿線下寫的完全是同一個東西(滑稽

==============分鴿線=================================

瀉藥

調試有啥技巧啊。。?

用腦子寫代碼的話,可能調試也不是很需要吧。。?

要是沒有腦子,那就該怎麼調怎麼調,感覺就行了吧。。?


把代碼刪了重寫


謝邀。談談我這兩年喜歡的調試方法?

我不會gdb(捂臉),所以我一般都只能輸出中間結果。

我代碼能力很菜,所以我傾向於在寫數據結構的時候,先寫一個debug函數。用來方便調試。

void debug() //splay
{
int i;
for(i=0;i&<=tot;i++) printf("Node[%d] le=%d rt=%d key=%d value=%d ",i,le[i],rt[i],key[i],value[i]); }

我寫數據結構,一般是寫幾個函數就調一次,爭取中途就把事情解決掉,不要寫完了再調。

然後我就習慣了這套搞法……

另外一個方法,用來調段錯誤的,之前在別的回答里說過:

#define DEBUG printf("Passing [%s] in LINE %d
",__FUNCTION__,__LINE__)

在每個函數的入口處執行一次,出口處執行一次。然後就可以快速得知是哪個地方段錯誤了。

關於調數論題。有幾個經驗可以談。

首先是負數……如果見到負數那就大概是爆int了,這時我一般把所有的int都改成ll。

如果出現別的問題,我一般的做法是手算一遍。然後打出中間結果,看是從哪裡開始和手算不合的。


說一件真事;

我們的隔壁機房都是大佬;

zjoi2017的時候;

某大佬怒剛第二題,結果代碼寫萎了;

然後他對自己說;

如果我11:00還沒調出來我就重構代碼;

然後11:00到了

然後他說:如果我12:00還沒調出來就重構代碼;

然後12:00到了;

然後他花了20分鐘的時間重構了代碼;

傻逼數據結構題ac了;

進隊了........

另外就是調試前一定要存檔備份!!!!


1. 操作函數化,分塊測試,能獨立測試的演算法部分先驗證其正確性。

2. 肉眼查錯,對於寫了很多次的數據結構之類的東西是有感覺的,比如哪裡容易出問題,哪裡容易寫錯,可以有重點地看。

3. 在關鍵位置輸出tag,比如在每個分部操作之後輸出可以手算驗證正確性的關鍵數據,通過前後輸出比對可以比較快找出問題出現在哪個模塊,縮小範圍。

4. 對拍,這個不用多講,oi選手應該都知道用bat,cmd之類將暴力代碼和你認為的標算比對,可能,這應該也算是一種調試技巧吧。

5. 重寫,在acm比賽中如果代碼出問題了,換個人重寫有時候在效率上反而比查錯更快,oi也差不多吧,完全拋棄,然後重新開檔寫吧。

蒟蒻意見,大牛繞道。


小黃鴨調試法(逃


把代碼清空,重新寫一遍,錯誤...就沒了?

這魔幻的辦法一般情況下都是有用的,這可能和我的思維特點有關係吧..

寫代碼的時候要先過一遍腦子


靜態查錯

肉眼觀察

動態查錯

找一份大神的代碼,和他寫成一樣,然後不停地在自己的代碼和大神代碼之間切換,肉眼比對代碼之間的不同。

二分查錯

找一份大神的代碼,和他寫成一樣,然後用大神的一段代碼替換自己的,如果還是錯的,則說明錯誤不在這裡。


dp題就眼調+重新推導。

數據結構題就手造幾個數據模擬,輸出中間結果進行比較。

幾何題,各憑本事吧。


實在不行,上vs


我一般是肉眼調試。。。


首先一個是代碼習慣上,善用assert,但凡代碼流程中有一些假設的時候,適當使用assert來檢查一下。它們可以節約你很多時間來找出出錯的地方和原因。這些東西在不要的時候用NDEBUG關掉即可

對於比較側重於過程的代碼(如數論、花式迭代、數據結構的操作流程、出現了Segfault的時候等),推薦使用gdb一類可以斷點跟蹤的調試器。我一般會用gdb打幾個斷點,或者定幾個trace監控一下,對遞歸函數用bt命令看調用棧。

對於數據量比較大的代碼(如數據結構本身、DP、爆搜、使用大量指針的代碼、迭代次數較多等),gdb可能會顯得比較雞肋。這時候需要自己將所需要的東西輸出,然後再慢慢分析。通常使用DEBUG宏輸出到stderr中。如果輸出量太大就將stderr重定向到文件中。DEBUG宏同樣也可以實現用NDEBUG宏來作為開關。

最後,如果以上方法沒有解決你的問題,請果斷執行以下操作:

Ctrl-A + Delete

另外注意代碼備份。

就是這樣QAQ


OI 調試技巧

&> 2o17.8.6 By gwj

&> QAQ全部臨時寫的,代碼沒有測試過正確性什麼的

+ 1 小黃鴨調試法

來自維基:小黃鴨調試法是軟體工程中使用的調試代碼方法之一。

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

+ 2 輸出中間值

在關鍵位置輸出值

  1. 適用於以下一些:
  2. 數據輸入,輸出
  3. 死循環,盞溢出
  4. 過程值,語義分析(較痛苦

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
"
else
printf "Wa
"
exit 0
fi
done
————————————————————————
//Windows 數據生成器 .cpp
#include&
#include&
#include&
#include&
#include&
using namespace std;
#define random(a, b) ((a)+rand()%((b)-(a)+1)) //Integer[a,b]
int main(int argc, char *argv[]){
//1. 隨機數初始化
stringstream ss;
int seed = time(NULL);
if(argc){
ss&<&&>seed;
}
srand(seed);
//2. 數據生成
int T = random(1, 41);
while(T--){
int rt=random(2,8), bx=random(1,9), by=random(1,10);
cout&<&

+ 5 靜態查錯

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

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

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

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

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

要保證程序無錯,思路清晰,結構清晰了,然後再去測樣例,再去測特殊數據。

樣例過了不要得意,特殊數據過了不要得意,很有可能還有很多特殊情況你沒有想到。

+ 6 其他一些

1. 重寫代碼(霧

2. 拒絕調試(逃


依我的話。。。千年人肉Debug但是真的不會時我會啟用GDB大法,但是很多時候不想GDB就直接重構了。經常會遇到調試兩小時不如重構30分鐘的事情。

對了,關於小黃鴨調試法,我一般會選擇寫注釋。。。。。感覺說一遍不如寫一遍令人專註。


對於不同的情況有不同解

1.如果各位都是像YJQ一樣的dalao當然拒絕調試最好。

2.對於OI的新人來說調試必然是一個非常磨人而又不得不做的事情,對於他們掌握調試技巧就十分重要。我個人推薦的方式是最好不要對比大神代碼或者重寫,分段調試是墜吼的。


實踐證明,重寫相當有效。


DUMP ALL THE FUCKING VARIABLES


推薦閱讀:

為什麼完全聽不懂冬令營講課的蒟蒻還要去CCF NOI 冬令營?
在國際奧賽中取得好成績的那些大牛們都很愛賣萌嗎?怎樣利用學術賣出一個高格調的萌?
OIer如何找到一個妹子?

TAG:OI | NOIP |