c語(yǔ)言軟件編程規(guī)范工作組_第1頁(yè)
已閱讀1頁(yè),還剩42頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、C語(yǔ)言軟件編程規(guī)范工作組,公司常見(jiàn)軟件編程低級(jí)錯(cuò)誤:內(nèi)存泄漏,前言,這套材料作為編程規(guī)范的輔助材料,幫助大家理解編程規(guī)范背后的原理。C和C++語(yǔ)言是我司的主流編程語(yǔ)言,然而C/C++具有很多強(qiáng)大的語(yǔ)言特性,從而導(dǎo)致C/C++非常復(fù)雜,使得代碼更容易出現(xiàn)BUG、難以閱讀和維護(hù)。業(yè)界知名的編程規(guī)范都對(duì)C/C++容易出現(xiàn)問(wèn)題的語(yǔ)言特性進(jìn)行管理。例如MISRA(汽車(chē)工業(yè)軟件可靠性聯(lián)合會(huì))制定的1998版的MISRAC規(guī)范指出,一些在C看來(lái)可

2、以接受,卻存在隱患的地方有127處之多。2004版的MISRAC規(guī)范將針對(duì)C語(yǔ)言的規(guī)則增加到了141條。對(duì)于程序員來(lái)說(shuō),能工作的代碼并不等于“好” 代碼?!昂谩贝a的指標(biāo)很多,包括可讀性、可維護(hù)性、可移植性和可靠性等。出現(xiàn)網(wǎng)上問(wèn)題的代碼,大多數(shù)是不良編程習(xí)慣引起的。不遵守編程規(guī)范的代碼,往往也是最不可靠的代碼。本膠片收集了常見(jiàn)的內(nèi)存泄漏案例,給出了相應(yīng)的糾正措施。對(duì)應(yīng)的編程規(guī)范:防止內(nèi)存泄漏;函數(shù)中分配的內(nèi)存,在函數(shù)退出之前要釋放

3、,內(nèi)存泄漏案例問(wèn)題和糾正措施建議,異常出口處沒(méi)有釋放內(nèi)存,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:pMsgDB_DEV = (PDBDevMsg)GetBuff( sizeof( DBDevMsg ), __LINE__);if( pMsgDB_DEV == NULL ) {return;} pMsgDBApp_To_Logic = (LPDBSelfMsg)GetBuff( sizeof(DBSelfMsg), __L

4、INE__ );if( pMsgDBApp_To_Logic == NULL ) {return;}【問(wèn)題定位】在第2個(gè)return處,pMsgDB_DEV指向的內(nèi)存丟失,異常出口處沒(méi)有釋放內(nèi)存 (續(xù)1),【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:/* 在AVL樹(shù)中添加節(jié)點(diǎn) */IF_VC_AVL_AddNode( &stAVLNodeKey, pstBasPortIndex, IF_VC_AVL_CompareNode)

5、ulRet = IF_BAS_VC_CreateVC((*pstBasPortIndex),ulIfIndex);if (ulRet != VOS_OK ) /* 創(chuàng)建VC控制塊失敗 */{      return;} 【問(wèn)題定位】創(chuàng)建VC控制塊失敗時(shí),return前沒(méi)有刪除AVL樹(shù)中的節(jié)點(diǎn)【舉一反三】看見(jiàn)return要注意,要去前面找資源,特別要注意鏈表等數(shù)據(jù)結(jié)構(gòu)中的資源

6、,異常出口處沒(méi)有釋放信號(hào)量資源,【問(wèn)題描述&定位】代碼飛檢發(fā)現(xiàn)如下代碼:rc = np_ss_semB_create(NP_SEM_EMPTY, NP_SEM_Q_FIFO, (g_ims_vport_base_info.qinq_base_info.sem)); if(rc != NP_RC_SUCCESS)/*申請(qǐng)信號(hào)量失敗*/ { NP_SS_ASSERT(0, "Create qinq sem

7、 failed!"); return rc; } #if SOFT_Versionrc = np_ss_semB_create(NP_SEM_EMPTY, NP_SEM_Q_FIFO, &(g_ims_vport_base_info.eqinq_base_info.sem));if(rc != NP_RC_SUCCESS)/*申請(qǐng)信號(hào)量失敗*/ { NP_SS_ASS

8、ERT(0, "Create qinq sem failed!"); return rc; //沒(méi)有釋放前面分配的信號(hào)量g_ims_vport_base_info.qinq_base_info.sem} #endif ………………………….NP_FREE_SEM(g_ims_vport_base_info.qinq_base_info.sem); #if SOFT_Version

9、NP_FREE_SEM(g_ims_vport_base_info.eqinq_base_info.sem); #endif return rc;,異常出口處沒(méi)有釋放信號(hào)量資源(續(xù)),【糾正措施】第二個(gè)信號(hào)量申請(qǐng)失敗return之前釋放第一個(gè)申請(qǐng)的信號(hào)量。。。。。。。。。。。。#if SOFT_Versionrc = np_ss_semB_create(NP_SEM_EMPTY, NP_SEM_Q_FIFO,

10、&(g_ims_vport_base_info.eqinq_base_info.sem));if(rc != NP_RC_SUCCESS)/*申請(qǐng)信號(hào)量失敗*/ { NP_SS_ASSERT(0, "Create qinq sem failed!"); NP_FREE_SEM(g_ims_vport_base_info.qinq_base_info.sem);

11、 return rc; } #endif。。。。。。。。。。。。【舉一反三】看見(jiàn)return要注意,要去前面找資源,特別要注意信號(hào)量、定時(shí)器等資源,異常出口處沒(méi)有釋放GUI資源,【問(wèn)題描述】網(wǎng)上問(wèn)題案例: CBitmap bmp; CBitmap* pOldBmp;  bmp.LoadBitmap(IDB_MYBMP); pOldBmp = pDC->SelectObject( &

12、;bmp ); if( Something() ) {  return; }  pDC->SelectObject( pOldBmp );【問(wèn)題定位】return前沒(méi)有調(diào)用SelectObject()把pOldBmp選回pDC中,這會(huì)導(dǎo)致pOldBmp指向的HBITMAP對(duì)象發(fā)生泄漏。這個(gè)程序如果長(zhǎng)時(shí)間的運(yùn)行,會(huì)導(dǎo)致系統(tǒng)花屏【舉一反三】除了申請(qǐng)的內(nèi)存外,系統(tǒng)提供的其它資源,如文件句柄/Socket/隊(duì)列等也是

13、資源,使用完畢必須釋放,沒(méi)有釋放結(jié)構(gòu)的成員指針,【問(wèn)題描述】網(wǎng)上問(wèn)題案例:struct STORE_BUF_S{ ULONG ulLen; UCHAR *pcData;}STORE_BUF_T;void func(){ STORE_BUF_T *pstStorageBuff = NULL; //申請(qǐng)結(jié)構(gòu)內(nèi)存 //程序處理。。。 free(pstStorag

14、eBuff); return;}刪除了pstStorageBuff,但pstStorageBuff->pcData沒(méi)有刪除?!締?wèn)題定位】先刪除了pstStorageBuff,pstStorageBuff->pcData永遠(yuǎn)不可能被刪除了 【舉一反三】刪除結(jié)構(gòu)指針時(shí),必須從底層向上層順序刪除,沒(méi)有釋放數(shù)組的成員指針,【問(wèn)題描述】測(cè)試部對(duì)M產(chǎn)品進(jìn)行壓力和穩(wěn)定性測(cè)試,模擬文件上傳的場(chǎng)景,把本地目錄下的3萬(wàn)

15、個(gè)文件上傳到另一臺(tái)主機(jī)。發(fā)現(xiàn)上傳程序在傳送文件過(guò)程中,內(nèi)存在快速增長(zhǎng),通過(guò)ps auwx監(jiān)控,發(fā)現(xiàn)該進(jìn)程占用的內(nèi)存每隔4分鐘(一個(gè)周期)就突然增加20~30M的內(nèi)存?!締?wèn)題定位】struct dirent **namelist; int n = scandir(path.c_str(), &namelist, 0, alphasort);【1】 int i = 0; for(i ; i d_n

16、ame; free(namelist[i]); 【2】 if(name != ".." && name != ".") { ......... ++fileNum; if(MAX_SCAN_FILE_NUM == fileNum )//MAX_SCAN_FILE_NUM=10

17、00 { break; } } } free(namelist); 【3】 return ;,沒(méi)有釋放數(shù)組的成員指針(續(xù)),從上面的代碼可以看是指針數(shù)組namelist由系統(tǒng)函數(shù)進(jìn)行分配內(nèi)存(如【1】所示),內(nèi)存釋放時(shí)時(shí)分別由【2】【3】完成的。但是中間有個(gè)條件,每次只取1000個(gè)文件,如果目錄下的文件大于1000就跳出,后

18、面的就不會(huì)再管了(【2】沒(méi)有執(zhí)行到)。所以導(dǎo)致原來(lái)本地目錄下文件數(shù)比較小,小于等1000時(shí)沒(méi)有內(nèi)存泄漏;而當(dāng)本地目錄下的文件比較多,大于1000時(shí),就會(huì)導(dǎo)致內(nèi)存泄漏。【舉一反三】開(kāi)發(fā)人員在使用指針數(shù)組時(shí),要特別注意,確保在釋放數(shù)組時(shí),數(shù)組中的每個(gè)元素指針是否已經(jīng)提前被釋放了,這樣才不會(huì)導(dǎo)致內(nèi)存泄漏。,重復(fù)分配內(nèi)存,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:ULONG CQC_CmdNoDropLevelClass(VOID* pMsgRcv

19、,VOID** ppMsgSnd){/* 以下是從別處拷貝的代碼 */ulErrCode = CFG_CreateResMsgs(pMsgRcv, ppMsgSnd)); ………………/* 拷貝代碼結(jié)束 */………………ulErrCode = CFG_CreateResMsgs(pMsgRcv, ppMsgSnd)); ………………}【舉一反三】 代碼Copy要小心,沒(méi)有釋放傳入定時(shí)器的內(nèi)存,【問(wèn)題描述】使用puri

20、fy測(cè)試,發(fā)現(xiàn)上報(bào)MLK:在我們的應(yīng)用程序的對(duì)象中,由于在設(shè)置定時(shí)器時(shí)需要傳遞一個(gè)參數(shù),這個(gè)參數(shù)的需要從堆中去獲取,因?yàn)樵诤罄m(xù)的定時(shí)器超時(shí)回調(diào)時(shí)需要使用這個(gè)參數(shù)。在對(duì)象中設(shè)置定時(shí)器的代碼如下:this->setTimer(timerId, pending_user_enroll_timer_id, 30, 1, // 設(shè)置一個(gè)30秒的定時(shí)器,定時(shí)1次new TString(ipport));在定時(shí)器的超時(shí)處理程

21、序簡(jiǎn)化后如下:void Session::onTimeOut(TInt4 timerID, TInt4 userTimerID, void* pData){switch(userTimerID){ case pending_user_enroll_timer_id: {TString* ipport = reinterpret_cast(pData);...delete ipport; }...}}

22、在我們?cè)O(shè)置的定時(shí)器超時(shí)后,會(huì)自動(dòng)調(diào)用onTimeOut這個(gè)函數(shù),根據(jù)userTimerID來(lái)把所需要的參數(shù)轉(zhuǎn)化為我們?cè)瓉?lái)的數(shù)據(jù)類(lèi)型,使用完成后在銷(xiāo)毀它。這看起來(lái)一切都很正常,new出來(lái)資源通過(guò)delete來(lái)進(jìn)行釋放,為什么出現(xiàn)了內(nèi)存泄漏呢?,沒(méi)有釋放傳入定時(shí)器的內(nèi)存(續(xù)),【問(wèn)題定位】定時(shí)器設(shè)置之后在未超時(shí)的時(shí)候,這個(gè)定時(shí)器的所在的對(duì)象就結(jié)束了,定時(shí)器自然就消失了,也就是我們new出來(lái)的東西也就消失了,內(nèi)存泄漏就此產(chǎn)生了。【糾正措

23、施】使用智能指針進(jìn)行解決,資源的釋放操作由C++語(yǔ)言特性進(jìn)行保證(在對(duì)象的生命周期結(jié)束調(diào)用其析構(gòu)函數(shù)),具體方案如下:在該對(duì)象類(lèi)添加一個(gè)智能指針類(lèi)型的私有成員變量:private:std::auto_ptr timer_arg_ipport_;這樣,在該對(duì)象被銷(xiāo)毀時(shí),根據(jù)智能指針的特性,由timer_arg_ipport_這個(gè)變量持有的資源會(huì)自動(dòng)被釋放。這樣,我們就不用擔(dān)心資源的泄漏問(wèn)題了。相應(yīng)的,設(shè)置定時(shí)器的代碼就變?yōu)轭?lèi)似如

24、下了:timer_arg_ipport_ = std::auto_ptr(new TString(ipport));this->setTimer(timerId, pending_user_enroll_timer_id, 30, 1, // 設(shè)置一個(gè)30秒的定時(shí)器,定時(shí)1次timer_arg_ipport_.get());同時(shí),在onTimerOut函數(shù)里也不用進(jìn)行delete的調(diào)用了?!九e一反三】資源的分配與釋

25、放不在同一個(gè)地方,可以考慮使用智能指針,類(lèi)型轉(zhuǎn)換不當(dāng):刪除的對(duì)象類(lèi)型不正確,【問(wèn)題描述】網(wǎng)上問(wèn)題案例:void* CPtrList::RemoveHead(){ } // 返回為void* 無(wú)類(lèi)型方式的通用指針typedef struct tagPropInfo { // m_PropList中的節(jié)點(diǎn),其中的成員變量有自己的構(gòu)造和析構(gòu)函數(shù)CString PropName;CString PropValue;} Prop

26、Info;while (!m_PropList->IsEmpty()) { delete m_PropList->RemoveHead();}【問(wèn)題定位】調(diào)用delete m_PropList->RemoveHead()時(shí),C++編譯器認(rèn)為delete一個(gè)void*的對(duì)象,并沒(méi)有調(diào)用類(lèi)PropInfo的析構(gòu)函數(shù),導(dǎo)致內(nèi)存泄漏【糾正措施】 將m_PropList->RemoveHead()的返回值強(qiáng)

27、制進(jìn)行一次類(lèi)型轉(zhuǎn)換【舉一反三】盡量避免將多個(gè)功能寫(xiě)作一條語(yǔ)句中,如果上面的代碼使用了臨時(shí)變量:void* tempPropInfo = m_PropList->RemoveHead();delete (PropInfo*)tempPropInfo;則很容易發(fā)現(xiàn)tempPropInfo的類(lèi)型不正確,就不會(huì)忘記進(jìn)行類(lèi)型轉(zhuǎn)換了,對(duì)需要釋放的指針重新賦值,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:struct FileInfo * pd

28、bffile = new struct FileInfo; pfile = pdbffile; 【問(wèn)題定位】pfile不為空,pfile以前指向的內(nèi)存丟失了【舉一反三】盡量不要對(duì)指針重新賦值。對(duì)指針重新賦值,首先考慮設(shè)計(jì)是否合理;除建立鏈表等特殊情況外,不要將非空指針作為左值。,宏里面有return語(yǔ)句,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:頭文件中的宏定義如下:#define ACE_NEW_RETURN(POINTER,CON

29、STRUCTOR,RET_VAL) \do { POINTER = new CONSTRUCTOR; \if (POINTER == 0 ) { error = ENOMEM; return RET_VAL } \ } while (0 )C++文件中的語(yǔ)句如下:ACE_NEW_RETURN( g_pProcTimer,CNMTimer(PROC,ONE_SECOND),IM_NM_NEW_FAIL);ACE_NEW_

30、RETURN( g_pBPTimer,CNMTimer(PROC,FIVE_SECOND),IM_NM_NEW_FAIL)【問(wèn)題定位】當(dāng)?shù)诙€(gè)ACE_NEW_RETURN語(yǔ)句在宏里面出錯(cuò)直接return時(shí),第一個(gè)ACE_NEW_RETURN語(yǔ)句申請(qǐng)的內(nèi)存泄漏了!,宏里面有return語(yǔ)句(續(xù)1),【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:頭文件中的宏定義如下:#define ERROR_HANDLE_RETURN(CONDITION,RE

31、TURNVALUE){if(true==(CONDITION)){return RETURNVALUE;}}C++文件中的語(yǔ)句如下:NEW(pShell,MessageHandlerShell);……IRegisting* pRegisting = pService->getFeatureRegist() ; ERROR_HANDLE_RETURN(NULL==pRegisting, False); 【問(wèn)題定位】

32、一旦宏ERROR_HANDLE_RETURN執(zhí)行retrurn,前面分配的內(nèi)存pShell就會(huì)泄漏。 【舉一反三】小心使用有return語(yǔ)句的宏,確保前面資源已經(jīng)釋放。,宏里面有return語(yǔ)句(續(xù)2),【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:// 將Session從Busy Session池移出,放入Free Session池ERROR_HANDLE_RETURN(FALSE==this->pop(enum_session_po

33、ol_busy,pSession),FALSE);ERROR_HANDLE_RETURN(FALSE==this->onIdleSession(pSession), FALSE);ERROR_HANDLE_RETURN(False==this->push(enum_session_pool_free,pSession),False); 【問(wèn)題定位】一旦從第2個(gè)ERROR_HANDLE_RETURN處退出,pSessio

34、n已經(jīng)從Busy Session池移出,但并沒(méi)有放入Free Session池,也沒(méi)有被刪除【舉一反三】宏里面有return語(yǔ)句,很容易造成各種隱患,產(chǎn)品最好禁止這種做法。,C++的析構(gòu)函數(shù)沒(méi)有釋放內(nèi)存,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:CDBOperation::~CDBOperation() {} 【問(wèn)題定位】上面代碼忘記對(duì)類(lèi)成員指針刪除,導(dǎo)致內(nèi)存泄漏【糾正措施】 修改代碼為:CDBOperation::~CDBOper

35、ation(){ If (NULL != m_pmmlProcssTimeCost) { DELETE(m_pmmlProcssTimeCost); }}【舉一反三】類(lèi)或函數(shù)申請(qǐng)的資源應(yīng)該在退出時(shí)釋放,使用復(fù)雜語(yǔ)句或函數(shù):多個(gè)判斷條件寫(xiě)在一起,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:char* pszInfoBuf = VOS_NULLPTR;char* pszInfoBuf2 = VOS_N

36、ULLPTR;pszInfoBuf = (char*)VOS_Malloc(MID_BVLAN, ulBuffLen); pszInfoBuf2 = (char*)VOS_Malloc(MID_BVLAN, ulBuffLen);if ( (pszInfoBuf == NULL) || (pszInfoBuf2 == NULL ) ){return VOS_ERR;}【問(wèn)題定位】當(dāng)pszInfoBuf申請(qǐng)成功,但pszI

37、nfoBuf2申請(qǐng)失敗時(shí),進(jìn)入此分支,pszInfoBuf指向的內(nèi)存泄漏了 【舉一反三】不要將多個(gè)判斷條件寫(xiě)在一起 。,使用復(fù)雜語(yǔ)句或函數(shù):沒(méi)有及時(shí)釋放不使用的內(nèi)存,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:ULONG CQC_CmdNoApplySystem(VOID* pMsgRcv,VOID** ppMsgSnd)代碼太長(zhǎng),這里不引用?!締?wèn)題定位】該函數(shù)共531行,函數(shù)開(kāi)頭就申請(qǐng)了pMsgRcv指針,從128行開(kāi)始,即不再使用這

38、個(gè)指針,但沒(méi)有及時(shí)釋放,一直到531行函數(shù)完全結(jié)束時(shí)才釋放pMsgRcv指針。但在128行以后,共有9處異常退出,有5處釋放了pMsgRcv指針,有4處卻沒(méi)有釋放pMsgRcv指針?!九e一反三】1。盡量避免過(guò)于復(fù)雜的函數(shù)2。養(yǎng)成及時(shí)釋放內(nèi)存和資源的習(xí)慣。,使用復(fù)雜語(yǔ)句或函數(shù):一個(gè)語(yǔ)句,多個(gè)分配,【問(wèn)題描述】網(wǎng)上問(wèn)題案例Fun(shared_ptr(new Widget), shared_ptr(new Widget))【問(wèn)題定

39、位】 函數(shù)Fun的兩個(gè)入?yún)⒍际侵悄苤羔?,?huì)被自動(dòng)回收,通常不可能出現(xiàn)內(nèi)存泄漏。但C++標(biāo)準(zhǔn)沒(méi)有強(qiáng)制規(guī)定函數(shù)參數(shù)的計(jì)算順序,不同的編譯器處理不同。編譯器可能將上面代碼細(xì)化操作為:對(duì)第一個(gè)參數(shù)執(zhí)行new Widget,分配內(nèi)存對(duì)第二個(gè)參數(shù)執(zhí)行new Widget,分配內(nèi)存對(duì)第一個(gè)參數(shù)執(zhí)行構(gòu)造函數(shù),實(shí)例化對(duì)第二個(gè)參數(shù)執(zhí)行構(gòu)造函數(shù),實(shí)例化如果第3步失敗,拋出異常,第1步申請(qǐng)的內(nèi)存被自動(dòng)回收,但第2步分配的內(nèi)存就丟失了【糾正措施】修改

40、代碼為:shared_ptrsp1(new Widget);shared_ptrsp2(new Widget);Fun(sp1,sp2);【舉一反三】不要在一條語(yǔ)句中分配一個(gè)以上的資源,應(yīng)該在自己的代碼語(yǔ)句中這些顯式的資源分配(比如new),而且每次都應(yīng)該馬上將分配的資源賦予管理對(duì)象(比如shared_ptr)。,非空指針作為左值:需要釋放的指針參與運(yùn)算,【問(wèn)題描述】網(wǎng)上問(wèn)題案例:while (NULL != (lpSrcSt

41、r = strchr(lpSrcStr,'.'))) { lpBackStr = lpSrcStr+1; …… if ((lpCheck255)) { free(lpSrcStr); return 0; } else { ulResult = (ulResult << 8 ) +lpChec

42、k; lpSrcStr = lpBackStr; // lpSrcStr指針發(fā)生了變化! lpForStr = lpSrcStr; }}free(lpSrcStr); 【問(wèn)題定位】 lpSrcStr參與運(yùn)算后,指向的地址已經(jīng)不是最初使用malloc申請(qǐng)的內(nèi)存了,free(lpSrcStr)失敗【舉一反三】使用臨時(shí)變量參與指針運(yùn)算,確保原指針不會(huì)被修改。需要釋放的指針禁止

43、參與運(yùn)算,缺少分支處理,【問(wèn)題描述】網(wǎng)上問(wèn)題案例:pMsg = g_InstantQueue.at(j);mu = SortOperation(pMsg);if (mu 5的分支,根本沒(méi)有考慮,內(nèi)存泄漏【舉一反三】if條件的所有分支都必須考慮清楚,沒(méi)有釋放隊(duì)列中的內(nèi)存,【問(wèn)題描述】前面同一個(gè)案例:pMsg = g_InstantQueue.at(j);mu = SortOperation(pMsg);if (mu <

44、= 0){ret = 0; }elseif (mu <= 5){ret = SendMsgToMu(pMsg, mu-1);}if (ret <= 0) {g_InstantQueue.removeAt(j);}【問(wèn)題定位】如果SendMsgToMu失敗(ret <= 0),則調(diào)用g_InstantQueue.removeAt(j)從消息隊(duì)列中移走消息,但并沒(méi)有釋放消息申請(qǐng)的內(nèi)存 【舉一反三】

45、處理鏈表或隊(duì)列時(shí),如果刪除了其中的一個(gè)節(jié)點(diǎn),還必須同時(shí)釋放節(jié)點(diǎn)申請(qǐng)的內(nèi)存,申請(qǐng)內(nèi)存以后才進(jìn)行異常檢查,【問(wèn)題描述】代碼飛檢發(fā)現(xiàn)如下代碼:NEW(pMsg, TMsg) ……………..iFileCount++; if (iFileCount >100) { break;}if (True == cacheAlarm(pMsgPara->m_alarmReport)) {

46、DELETE(pMsg);} else { sendMessage(pMsg);//把消息掛入消息隊(duì)列中}【問(wèn)題定位】條件if (iFileCount >100)為真,導(dǎo)致break時(shí),內(nèi)存泄漏。 【糾正措施】將申請(qǐng)內(nèi)存的語(yǔ)句N(xiāo)EW(pMsg, TMsg)移動(dòng)到判斷語(yǔ)句if (iFileCount >100)后面【舉一反三】如果在程序塊中存在異常檢查或退出機(jī)制,應(yīng)放在最前面,這條控制語(yǔ)句前面的

47、代碼必須與異常檢查或退出條件相關(guān)。,基類(lèi)中沒(méi)有定義虛析構(gòu)函數(shù),【問(wèn)題描述】代碼飛檢人員發(fā)現(xiàn)某產(chǎn)品缺陷跟蹤庫(kù)中這樣解決某網(wǎng)上問(wèn)題:class A { // virtual ~A() {} // 問(wèn)題定位:析構(gòu)函數(shù)中打印Log信息內(nèi)存越界,刪除析構(gòu)函數(shù)}【問(wèn)題定位】檢查Class A的子類(lèi):class B: public A{ ~B() {……}; // 子類(lèi)B定義了自己的析構(gòu)函數(shù)}; 如果出現(xiàn)下面的代碼:

48、A * p = new B; delete p;聲明的對(duì)象p的類(lèi)型為基類(lèi)A,但實(shí)際類(lèi)型為子類(lèi)B,所以delete p時(shí),編譯器認(rèn)為刪除的是Class A的對(duì)象,則調(diào)用Class A的析構(gòu)函數(shù)(編譯器對(duì)A添加的默認(rèn)析構(gòu)函數(shù)),而不調(diào)用B的析構(gòu)函數(shù),這樣B里面申請(qǐng)的內(nèi)存就泄漏了【糾正措施】在基類(lèi)A中增加一個(gè)空的虛析構(gòu)函數(shù),則編譯器會(huì)自動(dòng)調(diào)用B的析構(gòu)函數(shù)【舉一反三】只要在類(lèi)中有虛函數(shù),就必須有虛析構(gòu)函數(shù),基類(lèi)中沒(méi)有定義虛析構(gòu)函數(shù)(續(xù))

49、,【問(wèn)題描述】使用purify發(fā)現(xiàn)如下代碼內(nèi)存泄漏:void CAlarmer::destroyAlarmInfo(IAlarmInfo* pAlarmInfo) DELETE(pCAI);}【問(wèn)題定位】為了減少耦合,對(duì)外暴露的是IAlarmInfo的純虛接口。但內(nèi)部的實(shí)現(xiàn)是CAlarmInfo。在創(chuàng)建時(shí)創(chuàng)建一個(gè)CAlarmInfo而后強(qiáng)制轉(zhuǎn)換成IAlarmInfo返回。典型的使用基類(lèi)指針進(jìn)行刪除,由于基類(lèi)沒(méi)有設(shè)置虛析

50、構(gòu)函數(shù),導(dǎo)致派生類(lèi)的析構(gòu)函數(shù)沒(méi)有調(diào)用,這樣派生類(lèi)CAlarmInfo里面申請(qǐng)的內(nèi)存就泄漏了?!炯m正措施】在基類(lèi)IAlarmInfo中析構(gòu)函數(shù)設(shè)置為虛【舉一反三】允許通過(guò)基類(lèi)的指針進(jìn)行刪除操作,則基類(lèi)的析構(gòu)函數(shù)必須是公用且虛擬。,未及時(shí)加入列表,異常拋出時(shí)內(nèi)存泄漏,【問(wèn)題描述】CPArray類(lèi)為管理對(duì)象指針的列表,具有自動(dòng)釋放功能。程序正常情況下,加入CPArray中的對(duì)象可以得到釋放,在有異常發(fā)生的時(shí)候,對(duì)象的不到釋放,造成內(nèi)存泄

51、漏?!締?wèn)題定位】分析下面的代碼段,發(fā)現(xiàn)new的pObj已經(jīng)放到了自動(dòng)釋放列表中,粗看代碼沒(méi)什么問(wèn)題;...CPArray myArray(AUTO_FREE);CMyObject *pObj = NULL;for(int i = 0; i m_ID = i;... Function1(pObj); ... myArray.Add(pObj);}...跟蹤Function1函數(shù),在該函數(shù)內(nèi)有異常拋

52、出,導(dǎo)致myArray.Add(pObj)語(yǔ)句沒(méi)有被執(zhí)行,造成pObj對(duì)象沒(méi)有被釋放。,未及時(shí)加入列表,異常拋出時(shí)內(nèi)存泄漏(續(xù)),【糾正措施】對(duì)象創(chuàng)建以后,首先加入到自動(dòng)釋放列表中,再執(zhí)行其他操作;以上代碼修改為:...CPArray myArray(AUTO_FREE);CMyObject *pObj = NULL;for(int i = 0; i m_ID = i;... Function1(pObj);

53、 ...}... 無(wú)論函數(shù)Function1()是否有異常,都能保證對(duì)象得到釋放?!九e一反三】使用自動(dòng)釋放列表的程序,要第一時(shí)間將新創(chuàng)建對(duì)象加入到列表中;調(diào)用函數(shù)時(shí),要考慮到函數(shù)是否會(huì)拋出異常。,沒(méi)有釋放隊(duì)列中的內(nèi)存,【問(wèn)題描述】發(fā)現(xiàn)加載**軟件命令出現(xiàn)內(nèi)存泄漏現(xiàn)象?!締?wèn)題定位】分析下面的代碼段,發(fā)現(xiàn)刪除隊(duì)列節(jié)點(diǎn)時(shí),沒(méi)有刪除節(jié)點(diǎn)對(duì)應(yīng)的內(nèi)存。。。。。。。。。。。。。。//獲得加載命令附加信息指針CDldAllSwAd

54、dInfo *pDldAllSwInfo = (CDldAllSwAddInfo *)(g_TaskId.GetDataPtr(iTaskId));......//清除已經(jīng)完成加載的單板信息pDldAllSwInfo->m_pBoardReportList.RemoveAt(0);pDldAllSwInfo->m_pBoardReportList.SetSize(iSize-1);pDldAllSwInfo->

55、;m_pBoardReportList.FreeExtra();,沒(méi)有釋放隊(duì)列中的內(nèi)存(續(xù)),【糾正措施】添加如下代碼,問(wèn)題解決:if(pDldAllSwInfo->m_pBoardReportList[0] != NULL){ delete pDldAllSwInfo->m_pBoardReportList[0]; pDldAllSwInfo->m_pBoardReportList[0] = NU

56、LL;}pDldAllSwInfo->m_pBoardReportList.RemoveAt(0);pDldAllSwInfo->m_pBoardReportList.SetSize(iSize-1);pDldAllSwInfo->m_pBoardReportList.FreeExtra();【舉一反三】處理鏈表或隊(duì)列時(shí),如果刪除了其中的一個(gè)節(jié)點(diǎn),還必須同時(shí)釋放節(jié)點(diǎn)申請(qǐng)的內(nèi)存,重復(fù)連接數(shù)據(jù)庫(kù),【問(wèn)題描述】B0

57、20版本中,對(duì)****模塊進(jìn)行系統(tǒng)測(cè)試。某個(gè)VOD處于上載中,點(diǎn)擊,刷新其狀態(tài),則頁(yè)面運(yùn)行非常慢,最終出現(xiàn)錯(cuò)誤提示頁(yè)面。【問(wèn)題定位】在****方法的synchronizeCAStatus(ArrayList)方法中,在循環(huán)體內(nèi)new Operater(),即在循環(huán)體中創(chuàng)建數(shù)據(jù)庫(kù)連接try{ for (int index = 0; index < vodCAList.size(); index++) {

58、 dbOper = new DBOperator(); 。。。。。。。。。。。。。。 }}catch (Exception e){ 。。。。。。。。。。。。。。}finally{ dbOper.close();},重復(fù)連接數(shù)據(jù)庫(kù)(續(xù)),【糾正措施】數(shù)據(jù)庫(kù)連接不能放在循環(huán)體中創(chuàng)建,否則會(huì)導(dǎo)致內(nèi)存泄漏,數(shù)據(jù)庫(kù)連接異常。將dbOper = new DBOperator();

59、語(yǔ)句提前到循環(huán)體外,try語(yǔ)句內(nèi)try{ dbOper = new DBOperator(); for (int index = 0; index < vodCAList.size(); index++) { 。。。。。。。。。。。。。。 }}catch (Exception e){ 。。。。。。。。。。。。。。}finally{

60、 dbOper.close();},對(duì)非POD對(duì)象進(jìn)行memset操作,【說(shuō)明】POD(Plain Old Data)是普通舊式數(shù)據(jù),如結(jié)構(gòu)/枚舉/成員指針等,內(nèi)存字節(jié)是連續(xù)的。非POD類(lèi)的內(nèi)存字節(jié)不連續(xù),中間可能包含vptr(虛函數(shù)指針表)等隱藏的數(shù)據(jù)【問(wèn)題描述】網(wǎng)上問(wèn)題案例shared_ptr p1(new int ),p2(new int ) ; memcpy(&p1,&p2,sizeof(p1); 【

61、問(wèn)題定位】?jī)?nèi)存泄漏:p2不會(huì)刪除;內(nèi)存訪問(wèn)失敗:p1刪除兩次【舉一反三】 C++編譯器經(jīng)常會(huì)在多態(tài)對(duì)象中嵌入一些隱藏?cái)?shù)據(jù)(如vptr),多重繼承/虛擬繼承會(huì)添加更多的內(nèi)部指針。不要直接對(duì)C++的類(lèi)進(jìn)行memset/memcpy等操作,應(yīng)盡量使用類(lèi)自身提供的賦值/復(fù)制功能。,強(qiáng)制關(guān)閉線程:沒(méi)有釋放線程占據(jù)的資源,【問(wèn)題描述】網(wǎng)上問(wèn)題案例CShakeHand::CShakeHand(){m_hdShakeThreadrecv = C

62、reateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc_ShakeHands,this, NULL, &m_ulShakeThreadID);}CShakeHand::~CShakeHand(){TerminateThread(m_hdShakeThreadrecv, 0); //強(qiáng)制關(guān)閉CloseHandle(m_hdShakeThreadrecv

63、);}【問(wèn)題定位】線程被強(qiáng)制關(guān)閉,導(dǎo)致線程內(nèi)部資源/內(nèi)存泄漏【舉一反三】使用事件或信號(hào)量通知線程,確保線程調(diào)用自身的退出函數(shù),預(yù)防內(nèi)存泄漏的方法:RAII,RAII是英文Resource Acquisition Is Initialization的縮寫(xiě)。意為在對(duì)象構(gòu)造時(shí)獲取資源,接著控制對(duì)資源的訪問(wèn)使之在對(duì)象的生命周期內(nèi)始終有效,最后在對(duì)象析構(gòu)的時(shí)候釋放資源。RAII有兩大好處:不需要顯式地釋放資源。即使是使用資源的方法的控制結(jié)

64、構(gòu)發(fā)生了改變,例如在中間插入return 或者拋出異常,我們能確定資源肯定會(huì)在對(duì)象析構(gòu)的時(shí)候被釋放。對(duì)象所需的資源在其生命期內(nèi)始終保持有效 —— 我們可以說(shuō),此時(shí)這個(gè)類(lèi)維護(hù)了一個(gè)invariant,通過(guò)該對(duì)象使用資源時(shí),不必檢查資源有效性,可以簡(jiǎn)化邏輯。我司有的項(xiàng)目組規(guī)定,禁止在函數(shù)中間退出,必須goto到函數(shù)結(jié)尾;在函數(shù)結(jié)尾統(tǒng)一釋放所有的資源。這是在C語(yǔ)言中變通實(shí)現(xiàn)RAII的方法,這種情況下允許使用goto語(yǔ)句。代碼飛檢發(fā)現(xiàn),

65、很多產(chǎn)品之所以被檢查出大量的內(nèi)存泄漏問(wèn)題,主要原因是釋放內(nèi)存的規(guī)則混亂。有的函數(shù)自己申請(qǐng)內(nèi)存,自己釋放;有的申請(qǐng)后,傳入子程序中,子程序使用并釋放;有的由子程序申請(qǐng)內(nèi)存并返回父程序,層層使用后在某一個(gè)函數(shù)內(nèi)釋放。華為公司編程規(guī)范要求:“規(guī)則9-5:過(guò)程/函數(shù)中分配的內(nèi)存,在過(guò)程/函數(shù)退出之前要釋放” 。RAII其實(shí)就是遵守這條規(guī)則的具體方法。通俗的說(shuō),就是:誰(shuí)申請(qǐng),誰(shuí)釋放,預(yù)防內(nèi)存泄漏的方法:使用C++類(lèi)庫(kù)的智能指針,auto_pt

66、r是標(biāo)準(zhǔn)C++庫(kù)提供的一種模板類(lèi),使用指針進(jìn)行初始化,其訪問(wèn)方式也和指針一樣。在auto_ptr退出作用域時(shí),所指對(duì)象能被隱式的自動(dòng)刪除。這樣可以方便的象使用普通指針一樣使用auto_ptr,而不用考慮釋放問(wèn)題。auto_ptr的復(fù)制會(huì)造成它本身的修改,原有的auto_ptr將不再指向任何對(duì)象,而由新的auto_ptr接管對(duì)象內(nèi)存,并負(fù)責(zé)自動(dòng)刪除。因此auto_ptr復(fù)制后不能再使用。且不能復(fù)制const auto_ptr。boos

67、t庫(kù)中提供了一種新型的智能指針shared_ptr,它解決了在多個(gè)指針間共享對(duì)象所有權(quán)的問(wèn)題,同時(shí)也滿足容器對(duì)元素的要求,因而可以安全地放入容器中。shared_ptr解決了auto_ptr復(fù)制語(yǔ)義破壞性。,截獲內(nèi)存申請(qǐng)/釋放函數(shù),添加調(diào)試信息,管理內(nèi)存的關(guān)鍵是截獲住對(duì)Free/Malloc/Relloc的調(diào)用。截獲住這幾個(gè)函數(shù),就能跟蹤每一塊內(nèi)存的生命周期,添加自己的調(diào)試信息。例如,可以添加如下的調(diào)試信息來(lái)管理內(nèi)存:每成功分配一

68、塊內(nèi)存,就把它的指針加入一個(gè)全局的list中,同時(shí)記錄申請(qǐng)內(nèi)存的文件名/行號(hào)等;每釋放一塊內(nèi)存,就把它的指針從list中刪除。程序結(jié)束的時(shí)候,list中剩余的指針就是那些沒(méi)有被釋放的內(nèi)存。例:使用VC++提供的CRT Library,修改源碼,編譯Debug版本,程序退出時(shí)打印所有未被釋放的內(nèi)存例:在不修改源碼,但可執(zhí)行文件存在調(diào)試信息時(shí),將應(yīng)用程序分發(fā)給Win32平臺(tái)的DbgHelp.Dll,通過(guò)Debug Help分析調(diào)用棧和堆

69、分配,檢測(cè)是否存在內(nèi)存泄漏問(wèn)題。申請(qǐng)內(nèi)存時(shí),多申請(qǐng)幾個(gè)字節(jié)。在這幾個(gè)字節(jié)中寫(xiě)入申請(qǐng)內(nèi)存的文件名/行號(hào)/申請(qǐng)時(shí)間等信息,測(cè)試時(shí)通過(guò)命令遍歷所有內(nèi)存,長(zhǎng)時(shí)間沒(méi)有被釋放的內(nèi)存就有內(nèi)存泄漏的嫌疑。例:打開(kāi)Dopra提供的內(nèi)存調(diào)試功能,最后由項(xiàng)目組人工分析沒(méi)有被釋放的內(nèi)存當(dāng)中哪些存在問(wèn)題申請(qǐng)內(nèi)存時(shí),多申請(qǐng)幾個(gè)字節(jié),寫(xiě)入保護(hù)信息;釋放內(nèi)存時(shí)檢測(cè)保護(hù)信息是否被改寫(xiě),若被改寫(xiě),則有內(nèi)存越界的問(wèn)題。例:自行截獲內(nèi)存申請(qǐng)和釋放的函數(shù),在申請(qǐng)的內(nèi)存前

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 眾賞文庫(kù)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論