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

下載本文檔

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

文檔簡介

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

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

3、,內(nèi)存泄漏案例問題和糾正措施建議,異常出口處沒有釋放內(nèi)存,【問題描述】代碼飛檢發(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;}【問題定位】在第2個return處,pMsgDB_DEV指向的內(nèi)存丟失,異常出口處沒有釋放內(nèi)存 (續(xù)1),【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:/* 在AVL樹中添加節(jié)點 */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;} 【問題定位】創(chuàng)建VC控制塊失敗時,return前沒有刪除AVL樹中的節(jié)點【舉一反三】看見return要注意,要去前面找資源,特別要注意鏈表等數(shù)據(jù)結(jié)構(gòu)中的資源

6、,異常出口處沒有釋放信號量資源,【問題描述&定位】代碼飛檢發(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)/*申請信號量失敗*/ { 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)/*申請信號量失敗*/ { NP_SS_ASS

8、ERT(0, "Create qinq sem failed!"); return rc; //沒有釋放前面分配的信號量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;,異常出口處沒有釋放信號量資源(續(xù)),【糾正措施】第二個信號量申請失敗return之前釋放第一個申請的信號量。。。。。。。。。。。。#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)/*申請信號量失敗*/ { NP_SS_ASSERT(0, "Create qinq sem failed!"); NP_FREE_SEM(g_ims_vport_base_info.qinq_base_info.sem);

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

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

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

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

15、個文件上傳到另一臺主機。發(fā)現(xiàn)上傳程序在傳送文件過程中,內(nèi)存在快速增長,通過ps auwx監(jiān)控,發(fā)現(xiàn)該進程占用的內(nèi)存每隔4分鐘(一個周期)就突然增加20~30M的內(nèi)存?!締栴}定位】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 ;,沒有釋放數(shù)組的成員指針(續(xù)),從上面的代碼可以看是指針數(shù)組namelist由系統(tǒng)函數(shù)進行分配內(nèi)存(如【1】所示),內(nèi)存釋放時時分別由【2】【3】完成的。但是中間有個條件,每次只取1000個文件,如果目錄下的文件大于1000就跳出,后

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

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

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

21、序簡化后如下: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、在我們設(shè)置的定時器超時后,會自動調(diào)用onTimeOut這個函數(shù),根據(jù)userTimerID來把所需要的參數(shù)轉(zhuǎn)化為我們原來的數(shù)據(jù)類型,使用完成后在銷毀它。這看起來一切都很正常,new出來資源通過delete來進行釋放,為什么出現(xiàn)了內(nèi)存泄漏呢?,沒有釋放傳入定時器的內(nèi)存(續(xù)),【問題定位】定時器設(shè)置之后在未超時的時候,這個定時器的所在的對象就結(jié)束了,定時器自然就消失了,也就是我們new出來的東西也就消失了,內(nèi)存泄漏就此產(chǎn)生了?!炯m正措

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

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

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

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

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

28、bffile = new struct FileInfo; pfile = pdbffile; 【問題定位】pfile不為空,pfile以前指向的內(nèi)存丟失了【舉一反三】盡量不要對指針重新賦值。對指針重新賦值,首先考慮設(shè)計是否合理;除建立鏈表等特殊情況外,不要將非空指針作為左值。,宏里面有return語句,【問題描述】代碼飛檢發(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++文件中的語句如下: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)【問題定位】當(dāng)?shù)诙€ACE_NEW_RETURN語句在宏里面出錯直接return時,第一個ACE_NEW_RETURN語句申請的內(nèi)存泄漏了!,宏里面有return語句(續(xù)1),【問題描述】代碼飛檢發(fā)現(xiàn)如下代碼:頭文件中的宏定義如下:#define ERROR_HANDLE_RETURN(CONDITION,RE

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

32、一旦宏ERROR_HANDLE_RETURN執(zhí)行retrurn,前面分配的內(nèi)存pShell就會泄漏。 【舉一反三】小心使用有return語句的宏,確保前面資源已經(jīng)釋放。,宏里面有return語句(續(xù)2),【問題描述】代碼飛檢發(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); 【問題定位】一旦從第2個ERROR_HANDLE_RETURN處退出,pSessio

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

35、ation(){ If (NULL != m_pmmlProcssTimeCost) { DELETE(m_pmmlProcssTimeCost); }}【舉一反三】類或函數(shù)申請的資源應(yīng)該在退出時釋放,使用復(fù)雜語句或函數(shù):多個判斷條件寫在一起,【問題描述】代碼飛檢發(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;}【問題定位】當(dāng)pszInfoBuf申請成功,但pszI

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

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

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

40、代碼為:shared_ptrsp1(new Widget);shared_ptrsp2(new Widget);Fun(sp1,sp2);【舉一反三】不要在一條語句中分配一個以上的資源,應(yīng)該在自己的代碼語句中這些顯式的資源分配(比如new),而且每次都應(yīng)該馬上將分配的資源賦予管理對象(比如shared_ptr)。,非空指針作為左值:需要釋放的指針參與運算,【問題描述】網(wǎng)上問題案例: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); 【問題定位】 lpSrcStr參與運算后,指向的地址已經(jīng)不是最初使用malloc申請的內(nèi)存了,free(lpSrcStr)失敗【舉一反三】使用臨時變量參與指針運算,確保原指針不會被修改。需要釋放的指針禁止

43、參與運算,缺少分支處理,【問題描述】網(wǎng)上問題案例:pMsg = g_InstantQueue.at(j);mu = SortOperation(pMsg);if (mu 5的分支,根本沒有考慮,內(nèi)存泄漏【舉一反三】if條件的所有分支都必須考慮清楚,沒有釋放隊列中的內(nèi)存,【問題描述】前面同一個案例: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);}【問題定位】如果SendMsgToMu失敗(ret <= 0),則調(diào)用g_InstantQueue.removeAt(j)從消息隊列中移走消息,但并沒有釋放消息申請的內(nèi)存 【舉一反三】

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

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

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

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

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

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

51、漏?!締栴}定位】分析下面的代碼段,發(fā)現(xiàn)new的pObj已經(jīng)放到了自動釋放列表中,粗看代碼沒什么問題;...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)語句沒有被執(zhí)行,造成pObj對象沒有被釋放。,未及時加入列表,異常拋出時內(nèi)存泄漏(續(xù)),【糾正措施】對象創(chuàng)建以后,首先加入到自動釋放列表中,再執(zhí)行其他操作;以上代碼修改為:...CPArray myArray(AUTO_FREE);CMyObject *pObj = NULL;for(int i = 0; i m_ID = i;... Function1(pObj);

53、 ...}... 無論函數(shù)Function1()是否有異常,都能保證對象得到釋放?!九e一反三】使用自動釋放列表的程序,要第一時間將新創(chuàng)建對象加入到列表中;調(diào)用函數(shù)時,要考慮到函數(shù)是否會拋出異常。,沒有釋放隊列中的內(nèi)存,【問題描述】發(fā)現(xiàn)加載**軟件命令出現(xiàn)內(nèi)存泄漏現(xiàn)象。【問題定位】分析下面的代碼段,發(fā)現(xiàn)刪除隊列節(jié)點時,沒有刪除節(jié)點對應(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();,沒有釋放隊列中的內(nèi)存(續(xù)),【糾正措施】添加如下代碼,問題解決: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();【舉一反三】處理鏈表或隊列時,如果刪除了其中的一個節(jié)點,還必須同時釋放節(jié)點申請的內(nèi)存,重復(fù)連接數(shù)據(jù)庫,【問題描述】B0

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

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

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

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

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

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

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

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

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

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

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

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

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

溫馨提示

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

評論

0/150

提交評論