C++小知識(shí):用合適的工具來分析你的代碼
靜態(tài)代碼分析工具可簡(jiǎn)化編碼過程,檢測(cè)出錯(cuò)誤并幫助修復(fù)。有個(gè)國(guó)外團(tuán)隊(duì)檢測(cè)了 200 多個(gè) C/C++ 開源項(xiàng)目,包括了 Php、Qt 和 Linux 內(nèi)核等知名項(xiàng)目。于是他們每天分享一個(gè)錯(cuò)誤案例,并給出相應(yīng)建議。今天的案例來自 LibreOffice 項(xiàng)目。
錯(cuò)誤代碼
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { .... CreateThread( NULL, 0, ParentMonitorThreadProc, (LPVOID)dwParentProcessId, 0, &dwThreadId ); .... }
解釋:
很久以前,我曾經(jīng)在業(yè)余時(shí)間接過一些項(xiàng)目。有一次我就接了一個(gè)項(xiàng)目,但是最后沒有搞定。這個(gè)項(xiàng)目本身就有問題,但是當(dāng)時(shí)我并不知道。更麻煩的是,這個(gè)項(xiàng)目乍一看還蠻簡(jiǎn)單的。
其實(shí)就是在 DllMain 方法中,當(dāng)某些條件觸發(fā)時(shí),用 Windows API 函數(shù)實(shí)現(xiàn)一些功能。我記不太清楚要實(shí)現(xiàn)哪些功能了,但是肯定不難。
我花了大量時(shí)間做這個(gè)項(xiàng)目,但是代碼就是不工作。更糟糕的是,如果我創(chuàng)建一個(gè)標(biāo)準(zhǔn)的新應(yīng)用,這段代碼就沒問題,一旦我把代碼放到 DllMain 里去運(yùn)行就不行。簡(jiǎn)直是個(gè)謎,不是嗎?我最后還是沒有找出問題的根源。
多年以后的今天,我使用?PVS-Studio 開發(fā)工具后,我突然意識(shí)到當(dāng)年問題的原因。你瞧,其實(shí) DllMain 函數(shù)能正確執(zhí)行的操作非常有限,因?yàn)椋ê芏嗖僮饕蕾嚨模〥LL 庫(kù)并沒有被加載,所以你不能直接在 DllMain 里就直接執(zhí)行任意的操作。
我們現(xiàn)在有了診斷工具,可以提醒程序員在 DllMain 里直接用哪些操作是危險(xiǎn)的?,F(xiàn)在我終于明白那時(shí)程序不能運(yùn)行的原因了。
關(guān)于不能在 DllMain 里執(zhí)行哪些操作的更多細(xì)節(jié),可以查看(PVS-Studio)關(guān)于 V718 診斷信息的描述。
所以,上面那段 LibreOffice 的代碼片段很可能就無法工作。它能不能正常執(zhí)行完全要靠運(yùn)氣。
正確的代碼:
要修復(fù)這類錯(cuò)誤其實(shí)很難。你需要重構(gòu)整個(gè)代碼邏輯,讓 DllMain 函數(shù)里的操作越簡(jiǎn)潔越好。
建議:
對(duì)于這類問題并沒有什么特別的建議。你不可能什么都知道,每個(gè)人總有一天都會(huì)遇到類似的謎題。我認(rèn)為一個(gè)比較普遍的建議是這樣的:請(qǐng)仔細(xì)地閱讀和工作相關(guān)的各種文檔。但你還是要明白,人們無法預(yù)測(cè)每一個(gè)可能出現(xiàn)的問題。如果你把所有的時(shí)間都拿去閱讀文檔了,那又怎么有時(shí)間去編程呢?即使你已經(jīng)讀了很多頁的文檔,你也不確認(rèn)有沒有漏看了某個(gè)文檔,而它是可以讓你免于犯錯(cuò)的。
我希望能給出更加實(shí)用的建議(來避免這些難以捕捉的錯(cuò)誤),但是很遺憾我只能想到一條:使用靜態(tài)分析工具。當(dāng)然這還是不能保證你就不會(huì)犯錯(cuò)了。但是至少這么做會(huì)讓你犯錯(cuò)的幾率降低。如果當(dāng)年我有了這些工具,那我就絕對(duì)不會(huì)在 DllMain 里去調(diào)用我寫的那些代碼,那么我很可能就能節(jié)省大量時(shí)間,少死好多腦細(xì)胞。要知道,我對(duì)當(dāng)時(shí)沒能搞定那個(gè)任務(wù)一直耿耿于懷!
這個(gè)錯(cuò)誤由 PVS-Studio 靜態(tài)分析工具捕獲。錯(cuò)誤文本:V718:“CreateThread”方法不應(yīng)該在“DllMain”方法中調(diào)用。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
- C++實(shí)踐Time類中的運(yùn)算符重載參考方法
- C++實(shí)踐分?jǐn)?shù)類中運(yùn)算符重載的方法參考
- C++實(shí)踐排序函數(shù)模板項(xiàng)目的參考方法
- C++二維數(shù)組中數(shù)組元素存儲(chǔ)地址的計(jì)算疑問講解
- 劍指offer之C++語言實(shí)現(xiàn)鏈表(兩種刪除節(jié)點(diǎn)方式)
- Android Java調(diào)用自己C++類庫(kù)的實(shí)例講解
- Python使用ctypes調(diào)用C/C++的方法
- C++小知識(shí):不要去做編譯器的工作
- C++小知識(shí):C/C++中不要按值傳遞數(shù)組
- C++實(shí)踐數(shù)組類運(yùn)算的實(shí)現(xiàn)參考
相關(guān)文章
C語言之?dāng)?shù)組名與數(shù)組起始地址的關(guān)系解析
這篇文章主要介紹了C語言之?dāng)?shù)組名與數(shù)組起始地址的關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07C++利用代理模式實(shí)現(xiàn)遠(yuǎn)程代理,虛擬代理和保護(hù)代理
今天給大家簡(jiǎn)單介紹代理模式,一個(gè)很簡(jiǎn)單的設(shè)計(jì)模式,旨在不改變?cè)瓕?duì)象的情況下通過代理對(duì)象來控制對(duì)原對(duì)象的訪問。代理模式根據(jù)具體情況還可以分為遠(yuǎn)程代理、虛擬代理、保護(hù)代理等,下面來介紹一下2023-04-04C/C++?Qt?選擇夾TabWidget組件實(shí)現(xiàn)導(dǎo)航欄切換
Tab切換在很多地方都可以使用的到,本文就使用TabWidget組件來實(shí)現(xiàn)一下,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11C++實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07詳解C++ 編寫String 的構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值函數(shù)
這篇文章主要介紹了詳解C++ 編寫String 的構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、析構(gòu)函數(shù)和賦值函數(shù)的相關(guān)資料,這里提供實(shí)例幫助大家理解掌握這部分內(nèi)容,需要的朋友可以參考下2017-08-08C++報(bào)錯(cuò) XX does not name a type;
這篇文章主要給大家介紹了C++報(bào)錯(cuò) XX does not name a type;field `XX’ has incomplete type解決方案,文中通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下2023-08-08C語言實(shí)現(xiàn)求解最小公倍數(shù)的算法示例
這篇文章主要為大家介紹了C語言如何實(shí)現(xiàn)求解任意兩個(gè)正整數(shù)的最小公倍數(shù),文中采用了窮舉法和定理法。感興趣的小伙伴快來跟隨小編一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12