[Powered by Google Translate] [Valgrind的] [內特 - 哈迪森,哈佛大學] 這是CS50,CS50.TV] 在C程序中最困難的錯誤 來自管理不善的內存。 有數量龐大的方式來搞砸了, 包括分配錯誤的內存量, 忘記初始化變量, 寫入結束之前或之後,緩衝液, 和釋放內存保持多次。 症狀的範圍從間歇性崩潰 神不知鬼不覺覆蓋值, 往往是在遠離原來的錯誤的地點和時間。 追踪觀察到的問題的根本原因 可以是具有挑戰性的, 但幸運的是有一個有用的程序,稱為Valgrind的 ,可以做很多事情來幫助。 您在Valgrind下運行的程序,使 堆內存分配和訪問的廣泛的檢查。 Valgrind的檢測到問題時,它可以讓你即時, 直接的信息,讓您 更容易地找到和解決問題。 在Valgrind也不太致命的內存問題的報告, 如內存洩漏,堆內存分配, 忘記釋放它。 像我們的編譯器,鐺,在我們的調試器,GDB, Valgrind是,它是免費軟件,安裝在設備上。 Valgrind的上運行的二進制可執行文件, 不是你的C或H源代碼文件, 所以要確保你已經編譯到你的程序的最新副本 鐺或製作。 然後,在Valgrind下運行您的程序可以 作為,只是前綴的標準程序命令字Valgrind的簡單, 啟動Valgrind和它內部的運行程序。 當啟動時,Valgrind做一些複雜的 拉坯配置的內存檢查的可執行文件, 因此,它可以採取一個位來啟動和運行。 然後程序將正常執行,無論是速度要慢得多, 當它完成時,Valgrind會打印出其內存使用的摘要。 如果一切順利的話,它會是這個樣子: 在這種情況下,。/ clean_program 我想運行的程序的路徑。 而這一個不帶任何參數, 如果它這樣做,我只是粘性他們像往常一樣到最後的命令。 清潔程序僅僅是一個愚蠢的小程序,我 的整數塊在堆中分配空間, 放一些數值,並釋放內的整個塊。 這是你拍攝的,沒有錯誤,沒有洩漏。 另一個重要的指標是分配的字節數。 根據程序,如果你分配在MB或​​更高, 你可能做錯了什麼。 你不必要的存儲重複? 您是否使用的堆的存儲時,它會更好地使用堆棧? 因此,內存錯誤可能是真正的邪惡。 更加明顯的導致壯觀的崩潰, 但即便如此,它仍然是很難確定 究竟是什麼導致系統崩潰。 更陰險的是,一個程序,一個內存錯誤 仍然可以編譯 似乎仍然可以正常工作 因為你得到幸運的大部分時間。 經過多次“成功的結果,” 你可能認為,事故是僥倖的計算機, 但電腦是永遠不會犯錯。 運行Valgrind的可以幫助你追踪可見的內存錯誤的原因 以及潛伏的錯誤,你甚至不知道的問題。 每次Valgrind的檢測到問題時,它打印信息觀察。 每一個項目是相當簡潔 - 違規指令的源代碼行,是什麼問題, 和一點點的信息所涉及的內存 - 但往往是足夠的信息來將你的注意力到正確的位置。 下面是一個例子,Valgrind的運行在一個錯誤的程序 做一個無效的讀取的堆內存。 我們看到,在編譯沒有錯誤或警告。 嗯,哦,說有兩個錯誤錯誤摘要 - 兩個無效的讀取大小為4 - 字節,也就是。 這兩種壞的讀取發生在主函數中的invalid_read.c, 首先在第16行和第19行的第二個。 讓我們來看看在代碼中。 看起來像調用printf試圖讀取一個int過去的結束我們的內存塊。 如果我們回頭看Valgrind的輸出, 我們看到,正是Valgrind的告訴我們。 地址,我們嘗試讀取0字節開始 過去的結束的塊的大小為16個字節 - 4個32位的int值分配。 也就是說,我們試圖讀取的地址塊結束時,我們的開始, 正如我們看到在我們的壞printf調用。 現在,無效的讀操作可能看起來不是那麼大的交易, 但如果您使用的這些數據來控制你的程序的流量 - 例如,語句或循環的一部分,如果 - 接下來的事情可以靜靜地走壞。 觀看如何我可以運行invalid_read的程序 並沒有什麼不尋常的發生。 可怕的,是吧? 現在,讓我們來看看一些更種在你的代碼中的錯誤,你可能會遇到的, 我們會看到它們是如何Valgrind的檢測。 我們剛剛看到一個例子,一個invalid_read, 所以現在,讓我們看看一個invalid_write。 同樣,沒有編譯錯誤或警告。 好了,Valgrind的說,在這個程序有兩個錯誤 - 和invalid_write和invalid_read。 讓我們來看看這段代碼。 看起來我們已經有了一個實例,經典的strlen加一個錯誤。 該代碼不malloc的一個額外的字節的空間 / 0個字符, 所以,當STR複製去把它寫在ssubstrlen“CS50岩石!” 過去我們的塊寫1個字節。 該invalid_read時,我們使我們調用printf。 printf的閱讀無效的內存時,它會讀取/ 0個字符 因為它看起來在這個E弦的印刷。 但沒有逃脫Valgrind的。 我們可以看到,它抓住了invalid_write的STR副本 在第11行的主,和invalid_read的printf。 岩石上時,Valgrind。 同樣,這可能似乎不是什麼大不了的。 一遍又一遍以外的Valgrind的,我們可以運行這個程序 並沒有看到任何錯誤症狀。 然而,讓我們來看看在這方面的一個細微的變化看 如何可以得到非常糟糕的。 所以,理所當然的,我們是在濫用事情變得更不僅僅是一個位在這段代碼中。 我們只在堆上分配空間的兩個字符串 長度的CS50岩石, 這個時候,記住/ 0字符。 但後​​來我們扔在一個超長字符串的內存塊 S是指向。 什麼樣的影響會有多大的內存塊的T點? 那麼,如果T指向的內存就到S相鄰, 後, 然後我們可能已經寫了一部分,T. 讓我們運行此代碼。 看看發生了什麼事。 在我們的堆塊存儲的字符串,我們似乎已經正確地打印出來。 似乎沒有錯。 然而,讓我們重新回到我們的代碼和 註釋掉該行複製CS50岩石 到第二存儲器塊,指出由t。 現在,當我們運行此代碼時,我們應該 只看到第一個內存塊的內容打印出來。 哇,即使我們沒有STR副本 任何字符到第二堆塊,一個由T, 我們得到了一個打印出來。 事實上,字符串,我們塞進我們的第一個塊 衝出第一數據塊和到所述第二塊, 一切似乎正常。 Valgrind的,儘管告訴我們真實的故事。 我們走吧。 所有那些無效的讀取和寫入。 讓我們來看看另一種錯誤的一個例子。 在這裡,我們做的東西,而不幸的。 我們抓住一個int的堆空間, 我們初始化一個int - P - 指針指向該空間。 然而,當我們的指針被初始化, 的數據,它指向的只是垃圾是在這部分的堆。 所以,當我們加載的數據轉換成int我, 我們在技術上初始化i, 但我們這樣做,用垃圾數據。 調用斷言,這是一個方便的調試宏 適當命名的斷言庫中定義的, 將中止該程序,如果它的測試條件失敗。 也就是說,如果不為0,i是。 根據是什麼的堆空間,p指向的, 這個程序可能會工作,有時並未能在其他時間。 如果一切正常,我們只是幸運。 編譯器不會捕獲這個錯誤,但Valgrind的肯定會。 在那裡,我們看到從我們使用的垃圾數據所產生的誤差。 當您分配的堆內存,但不釋放或釋放, 被稱為洩漏。 對於一個小的,短命的運行的程序,並立即退出, 洩漏是相當無害的, 但對於較大的尺寸和/或壽命的項目, 即使是很小的洩漏可以複合成一些重大。 CS50,我們希望你 照顧釋放所有你分配的堆內存, 因為我們希望您建立的技能,要妥善處理好手工工藝 所要求的C. 要做到這一點,你的程序應該有一個確切的 一對malloc和free調用之間的對應關係。 幸運的是,Valgrind可以幫助你的內存洩漏。 這裡是有漏洞的程序,稱為leak.c分配 在堆上的空間,寫入,但不將其釋放。 我們編譯它的品牌和在Valgrind下運行, 我們可以看到,雖然我們沒有內存不足的錯誤, 我們確實有一個洩漏。 有16字節絕對丟失, 這意味著該內存的指針是不在範圍之內,當程序退出。 現在,Valgrind的不給我們的信息洩漏一噸, 但如果我們按照這個稍微注意一下,它提供了對底部的報告 重新運行 - 洩漏檢查全 洩漏的內存的全部細節, 我們會得到更多的信息。 ,在堆中摘要, Valgrind的告訴我們,失去了最初分配的內存。 正如我們知道在源代碼中, Valgrind的告訴我們,我們的內存洩露 leak.c的第8行上調用malloc分配 在主函數中。 相當漂亮的。 Valgrind的洩漏使用這些術語進行分類: 絕對迷路 - 這是堆分配內存 程序不再具有一個指針。 Valgrind的都知道,你曾經有過的指針,但已失去了它的軌道。 絕對是這個內存洩露。 間接失去了 - 這是堆分配內存 其中唯一的指針,它也被丟失。 例如,如果你失去了你一個鍊錶的第一個節點的指針, 然後第一個節點本身肯定會被丟失, 而會間接失去了任何後續節點。 可能丟失 - 這是堆分配內存 Valgrind可以無法確定是否有一個指針或不。 仍可達堆分配內存 該程序仍然在出口處有一個指針, 這通常意味著一個全局變量指向它。 要檢查這些洩漏的,你還必須包括選項 - 仍可達= 在你調用Valgrind的。 這些不同的情況下,可能需要不同的策略來清理它們, 但洩漏應該被淘汰。 不幸的是,固定洩漏是很難做到的, 因為不正確的電話免費的,可以吹你的程序。 例如,如果我們看invalid_free.c,, 我們看到壞的內存釋放的一個例子。 什麼應該是一個單一的通話,釋放的整個塊 的內存的int_block, 如今卻成為釋放每一個int大小的部分 單獨的存儲器。 這將失敗災難性的。 轟!什麼是錯誤。 這肯定是不好的。 如果你堅持這樣的錯誤,雖然,你不知道去哪裡找, 回到屬於你最好的朋友。 你猜對了 - Valgrind的。 Valgrind的,總是知道到底是什麼了。 alloc和自由的計數不匹配。 我們已經拿到了1 alloc和釋放。 和Valgrind也告訴我們,其中第一個壞的免費電話 - 一個觸發的爆破 - 來自 - 線16。 正如你看到的,不好的呼叫釋放是非常糟糕的, 因此,我們建議讓你的程序洩漏 當你的工作獲得正確的功能。 後,才開始尋找洩漏你的程序工作正常, 沒有任何其他的錯誤。 而這一切,我們已經有了這個視頻。 你還等什麼呢? 轉到現在對您的程序運行Valgrind的。 我的名字是Nate哈迪森。這是CS50。 [CS50.TV]