第1步,製作一切的副本。
將整個項目複製到某處。記下你製作該副本時的項目狀態以及日期:時間。不要編輯該副本。如果你願意,你甚至可以使文件不可寫入。你需要能夠看到你已經改變,並回到它。儘管該程序目前不能在Unix上運行,但它可以在Windows下運行,所以你知道它有一些優點,並且接近於有用的上網功能。當我在編寫程序時或在編譯器中感到不安時因爲沒有理解它(這種情況現在比10年前少得多)我傾向於失去所有我正在改變的東西,所以改變它變得困難。使用某種類型的版本控制(即使只是保留額外的副本)可以幫助你跟蹤你有什麼變化,所以當你犯了一個錯誤,你可以很容易地解決這個錯誤。差分工具,如diff
在您知道如何使用它們時非常有用。對於現在你可能會想嘗試:
diff --minimal --side-by-side --ignore-all-space old_file.c new_file.c | less
希望您使用的是支持這些選項,因爲我認爲他們可能是最有幫助的你,你有短時間內差異。如果你發現你需要在屏幕上顯示更多信息,並且你的終端窗口很大,你還可以添加--width=
命令,並在終端上爲其添加一行字符。
無論如何,製作並保留大量的代碼副本,直到你知道你不再需要它們(甚至可能)。
如果您有圖形訪問,請參閱kdiff3
是否可用。您可能更容易快速使用。名稱中的3表示能夠一次比較文件的3個版本(一個共同的起點和該文件的兩個編輯版本)並且很有用,但您可以稍後瞭解該文件。它完全能夠比較文件的兩個版本併產生不錯的輸出。
第2步不要忽略警告
我建議您用最高級別的警告可能你的編譯器編譯並DO不忽略任何警告。如果你已經有警告而沒有告訴編譯器發出更多的警告,那麼首先檢查它們。警告是有原因的,只是偶爾會遇到應該忽略的產生警告的代碼(甚至於我通常會添加關於預期警告類型的註釋以及它爲什麼不是錯誤)。使用gcc
,您可以將-Wall
選項添加到編譯命令以發出所有警告。
gcc -Wall my_program.c -o my_program
有些人可能沒有什麼意義,你,但你至少可以看看代碼,看看可能是什麼不清楚它在警戒線附近。代碼
步驟3用簡單的線條
東西,這將使警告容易理解的使用非常簡單易懂的代碼行。試圖在一行代碼中添加太多的功能使得對於該行代碼的任何警告或錯誤消息更加難以理解。
步驟4使用臨時變量
臨時變量並不一定意味着「我的程序使用更多的內存」,但他們往往意味着編譯器提供了更有意義的警告,因爲在表達式中的數據類型的變量是更清晰。
步驟5中使用的功能
這距離3層4的功能理念的延續使事情變得更容易理解。他們還會這樣做,以至於當您發現錯誤並修復錯誤時,您通常不必擔心在程序的其他位置有錯誤代碼的副本需要修復(儘管您仍然應該搜索類似的代碼確定)。
第6步斷言
有一個宏(如功能,但並不完全)呼籲assert
,生活在#include <assert.h>
,可以幫助你找到各種錯誤的通過使你的程序不能早於它,否則會。這聽起來很糟糕,但很多時候(特別是與分段錯誤(SIGSEGV)等與內存有關的問題)程序在它們死亡之前處於致命狀態。使用assert
可幫助您將他們的死亡轉移到較早的地方,以便您可以看到他們致命的錯誤是什麼,而不僅僅是看到它的結果。
assert
將布爾表達式作爲其參數 - 任何比較,整數,浮點數或指針都可以。任何你可以在if
或while
中用作條件的東西都可以。如果這個表達式爲假(0或NULL),那麼你的程序就會死在那裏,並且在很多系統上它會給你一個有用的錯誤信息,告訴你程序斷言的位置在哪裏,甚至可能是斷言是什麼。還有另一種有用的東西,這使我將在一點點談,但現在,使用斷言你只是做:
assert(x < y);
,如果x
不小於y
程序將中止(實際上調用abort
函數)。
這是搞什麼幫助:
int clear_buffer(char * buffer, unsigned len)
{ /* len should be size_t but I don't want to explain that right now */
assert(buffer);
memset(buffer, 0, len);
}
第7步,如果你的Unix系統有gdb
然後GREAT使用調試器
。如果沒有,你可能還有其他一些調試器,你可以學習如何使用。許多Unix C編譯器把-g
選項「包含調試符號」,所以添加到要傳遞到編譯器的其他選項並重新編譯程序,然後執行:
gdb ./myprogram
,它將打印一些東西然後提示你:
(gdb)
然後你可以設置斷點和各種好東西,但因爲你是在趕時間,讓崩潰只是做:
(gdb) r
在通常運行程序時,在程序後面加入任何參數。 gdb將運行你的程序,直到奇怪的事情發生。奇怪的是,在這種情況下,應該是一個SIGSEGV(當你試圖訪問它不應該訪問的內存地址時,UNIX對你的程序做了什麼)。 gdb會再次提示您(gdb)
。然後,你可以這樣做:
(gdb) bt
bt
代表回溯和GDB將打印出調用堆棧,這意味着被稱爲獲得當前函數的所有功能。您應該在底部附近看到main
。尋找頂部附近的第一個函數,它是你寫的函數。這是您需要開始嘗試查找錯誤的地方。如果在列表頂部的功能是不是你的一個然後嘗試發行:
(gdb) up
這將使它檢查調用堆棧上先前的功能。一旦你的功能之一說:
(gdb) list
它會告訴你一些代碼周圍的東西是錯誤的地區。
要退出GDB你這樣做:
(gdb) quit
而且回答Y,如果問你是否真的要退出。
如果您要使用assert
並且會導致程序死機,那麼您最終不會在調用堆棧上使用相當多的庫文件來混淆您。
令人遺憾的是,3,4和5混淆了從差異獲取更好信息的能力,因此我建議試圖限制您將此編程風格添加到已出現錯誤或警告(至少現在)的地方。
我希望這有助於
你提供信息的方式太少,對我們是有幫助... – darioo 2010-11-03 07:13:02
我不太確定你真的無意中發現了傳說中Heisenbug。請提供更多信息。代碼會很好,但編譯器警告等也可以幫助。 – 2010-11-03 07:15:54
也許你可以將有問題的部分粘貼到一些上下文中......順便說一下,在機器上沒有隨機的東西:P – 2010-11-03 07:17:36