以前一直用VS 2012來調(diào)試C/C++代碼,F(xiàn)5、F10、F11用起來甚是順手,前面也寫過一篇關于VS最好用的快捷鍵:Visual Studio最好用的快捷鍵(你最喜歡哪個), 所以對于調(diào)試C/C++代碼我一直鐘情于VS?勺罱螺d了一個linux環(huán)境下用C++編寫的開源庫,準備進行一番研究,由于我對gdb調(diào)試只處在初步 階段,還沒有對整個項目用gdb調(diào)試過,而且gdb調(diào)試看起來也不方便,還是VS看的直觀。為了省懶和省時間就將代碼弄到VS中進行編譯調(diào)試,結果發(fā)現(xiàn)編 譯不成功,因為里面出現(xiàn)了很多類似int block[2*n];這樣的變長數(shù)組。大家知道傳統(tǒng)C語言和C++是不支持變長數(shù)組功能的,不過在C99標準中新增的一項功能就是允許在C語言和C++ 中使用變長數(shù)組,節(jié)省了很多資源?珊薜氖,微軟的編譯器跟不上時代的步伐,C++11都出來這么久了,微軟到現(xiàn)在連C99還不完全支持(不知道最新版的 VS 2013支不支持),不知道是故意而為之還是其它什么原因。既然VS不支持變長數(shù)組,我這程序就調(diào)試不了,我也不可能一個個的把它改成定長的。后來想到用 Eclipse CDT進行調(diào)試,就下載了個完整的Eclipse CDT(沒在已有的Eclipse上安裝CDT插件而是下載了個完全用于C/C++開發(fā)的Eclipse,因為配置插件出現(xiàn)了很多問題,至今還待解決)。 Eclipse中的C/C++庫支持使用的是最新版的Cygwin,最新的g++肯定是支持變長數(shù)組的,這時也發(fā)現(xiàn)我下載的庫的原作者也是在 Eclipse CDT下開發(fā)該項目的,因為工程目錄下有.cproject和.project這兩個文件,因此認為在Eclipse CDT下編譯調(diào)試該工程是最佳選擇。經(jīng)過嘗試,編譯是通過了,可是運行時老是出現(xiàn)這樣一個錯誤:No source available for "ntdll!ZwWriteFile() at 0x77a4133a"。
然后各種google、百度都搜不到相關的信息或只有少數(shù)幾個沒什么價值的信息?磥碇荒軘帱c調(diào)試了,發(fā)現(xiàn)了問題所在位置:
<span style="font-size: 16px;"> if(i!=0){
re[i]='\0';
if (re[0]!= '#'){
j++;
if (j>=from && (to==-1 || j<=to)){
if (DEBUG) fprintf(stdout,"\n%d) processing regex:: <%s> ...\n",j,re);
parse_re(nfa,re);
}
}
free(re);
}
if (DEBUG) fprintf(stdout, "\nAll RegEx processed\n");
if (re!=NULL) free(re);
//handle -m modifier
if (m_modifier && (!anchored->get_epsilon()->empty() || !anchored->get_transitions()->empty())){
non_anchored->add_transition('\n',anchored);
non_anchored->add_transition('\r',anchored);
}
// delete non_anchored, if necessary
if(non_anchored->get_epsilon()->empty() && non_anchored->get_transitions()->empty()){
nfa->get_epsilon()->remove(non_anchored);
delete non_anchored;
}else{
non_anchored->add_any(non_anchored);
}
</span>
發(fā)現(xiàn)每次判斷該條件語句if (m_modifier...)過后才報上面那個錯誤,所以堅信是這條語句有問題,經(jīng)過一番檢查覺得這語句沒啥問題,無奈之下干脆將兩個判斷條件全部注釋掉了,結果還是出現(xiàn)問題,問題轉(zhuǎn)到注釋語句的下面,實在不清楚是啥原因,就仔細看了下“No source available for "ntdll!ZwWriteFile() at 0x77a4133a"這 條錯誤語句,發(fā)現(xiàn)是和ntdll庫有關,于是就搜ntdll庫錯誤相關的資料,最終發(fā)現(xiàn)可能是跟堆相關,可還是沒能解決問題。最終我還是轉(zhuǎn)到VS下面調(diào) 試,當然前提是去掉了變長數(shù)組(還好發(fā)現(xiàn)變長數(shù)組只出現(xiàn)在兩個文件的兩個函數(shù)中,直接注釋掉了),編譯成功后運行出現(xiàn)錯誤:
點Continue接著出現(xiàn)錯誤:
看了下錯誤信息真的是堆問題,調(diào)試下發(fā)現(xiàn)是這句if (re!=NULL) free(re);執(zhí)行不了,再次調(diào)試發(fā)現(xiàn)前面re這個對象已經(jīng)通過free(re)釋放了,這里按理說re應該為NULL了也就是不會再次 free(re)了啊,可是實際運行的確re不為NULL因此再次free了re,相當于一塊本來已經(jīng)釋放了的內(nèi)存空間再次被釋放,肯定會出現(xiàn)堆錯誤了。 將該條件語句注釋掉后,運行成功,然后在Eclipse下注釋掉該句也是運行成功,F(xiàn)在問題就來了:
1. 為什么free(re)過后re不為NULL呢?
我一直認為將一個對象free過后該對象就為NULL了,這樣就可以通過判斷該對象是 否為NULL來知道該對象是否為正確的釋放了,如果沒有釋放(上面的代碼中也就是if(i!=0)沒執(zhí)行)那么在此進行釋放以避免內(nèi)存泄露。這個工程庫中 也是這樣做的,可是通過調(diào)試卻發(fā)現(xiàn)不是這樣的情況,現(xiàn)在我能想到的唯一解釋就是:free(re)過后re所指內(nèi)存空間的確被釋放了,但re本身的值不會 改變,也就是形參的值沒有改變,所以re還是原來的值當然就不是NULL了,這樣后面的再次free也就會被執(zhí)行,但re所指的內(nèi)存已經(jīng)被釋放所以再次 free也就失敗了。如果這個解釋正確的話,那么以后怎么判斷re所指的內(nèi)存是否被釋放了呢?當然上面的代碼很好解決,直接在if(i!=0)后面加 else{ free(re); }也就解決了,可是其它情況呢?
2. Eclipse中為什么調(diào)試不出來這個錯誤呢?
Eclipse的調(diào)試功能也十分強大,可是這里的調(diào)試卻不友善,一個是錯誤信息看不 懂,一個是出錯位置調(diào)試不出來,雖然出錯位置就在調(diào)試出來的位置的正上面,但調(diào)試的時候if (re!=NULL) free(re);這句的確是執(zhí)行成功了,所以也就不會認為是這句的問題,難道程序真正的出錯位置是在Eclipse下調(diào)試出來的出錯位置的正上面嗎?額,應該不會吧。
下面不得不簡單比較下VS和Eclipse調(diào)試功能的差異:
1. 首先如果你習慣了用VS的調(diào)試,那么轉(zhuǎn)到Eclipse下可能會有些不太習慣,尤其是大家熟知的VS下的F5、F10、F11到了Eclipse下卻變成了F8、F6、F5,其它的也不同,這樣的轉(zhuǎn)變有時候真不習慣。
2. 我覺得Eclipse下調(diào)試有一點的確比VS好,就是對函數(shù)的智能提示,Eclipse下當你講鼠標放到一個自定義函數(shù)上面,會自動顯示該函數(shù)的實現(xiàn),而VS下只能顯示該函數(shù)的聲明,要知道定義還得按F12跳過去。
Eclipse下:
VS下:
其它的我就不多作比較了,比如快捷鍵方面,因為對VS快捷鍵較熟,對Eclipse快捷鍵還不是很了解(雖然自己最熟的語言是Java,但調(diào)試Java的次數(shù)較少),所以兩者快捷鍵方便的差異性我也就不太清楚了,如果清楚的麻煩告訴我。
好了,以自己親自調(diào)試的一 個小錯誤引出了這么一個問題:Eclipse與VS,你更喜歡哪個呢?當然有人會說,開發(fā)C/C++與C#就用VS,開發(fā)Java就用Eclipse,可 是Eclipse可不僅僅是Java的編輯器,Eclipse是全能型的,可以編譯常見的所有語言如C/C++、C#、Python、Ruby等等,如果 你鐘愛Eclipse,完全可以用它來開發(fā)你想要開發(fā)的任何程序。