1. 程式人生 > >指標的記憶體釋放

指標的記憶體釋放

好幾年沒用C/C++了,由於專案需要,重拾C/C++,使用中時不時的有種親切感湧來。由於C/C++的自由,讓人痛苦但更是讓人喜歡的原因,一些經驗和技巧顯得尤為重要。

    最近總是想不明白,一個函式返回一個記憶體或者物件的指標,那麼這個指標指向的物件(記憶體)到底由誰來釋放?要知道,函式返回指標,通常是給外部臨時使用的,其指向的記憶體通常由函式或者成員函式所在的物件自己來管理,其實就是遵從一個原則:自己的事自己做。

    如果返回一個函式中的臨時變數地址,很顯然,當執行return後,出了臨時變數的作用區,變數將被銷燬,這個指標指向的記憶體就被釋放掉了,系統可能在任何時候修改它,這個指標實際變成了野指標,外部使用它是相當危險的;

    如果函式是個類的成員函式,我們能想到的是在物件中申明一個指標,由物件來管理它,成員函式只管使用,但問題是,如果有很多這樣的函式,豈不是有很多這樣的、只為函式存在的一些奇奇怪怪的指標?

    實際上,解決辦法很簡單——使用靜態變數!——前面不是空了,而是白了,想看的,自己先想想答案,然後再想辦法看吧 :0

來段程式碼:

複製程式碼 // 取得Xml元素的值
// itemName:元素名
// buff:查詢緩衝區
// return:value字串char* XmlPack::getElementValue(char* itemName, char* buff)
{
    
staticchar value[64];

    
int itemLen = strlen(itemName);

    
char* itemStart =newchar[itemLen +3];
    memset(itemStart, 
'\0', itemLen +3);
    itemStart[
0='<';
    itemStart[itemLen 
+1='>';
    strncpy(itemStart
+1*sizeof(char), itemName, itemLen);

    
char* itemEnd =newchar[itemLen +4];
    memset(itemEnd, 
'\0', itemLen +4);
    itemEnd[
0='<';
    itemEnd[
1='/';
    itemEnd[itemLen 
+2='>';
    strncpy(itemEnd
+2*sizeof(char), itemName, itemLen);

    
char* pos1 = strstr(buff, itemStart);
    
if(pos1 != NULL)
    {
        pos1 
+= strlen(itemStart);
        
char* pos2 = strstr(pos1, itemEnd);
        
if(pos2 != NULL)
        {
            assert((unsigned 
int)(pos2-pos1)<sizeof(value));
            memset(value, 
'\0'sizeof(value));
            strncpy(value, pos1, pos2
-pos1);
            delete[] itemStart;
            delete[] itemEnd;
            
return value;
        }
    }
    delete[] itemStart;
    delete[] itemEnd;
    
return NULL;
}

複製程式碼

     但是需要特別注意的是,使用區域性靜態變數是執行緒不安全的,是一個不可重入函式。

     解決這個問題的標準、權威的做法還是呼叫者提供儲存空間,通過引數傳入地址,這樣,呼叫者可以使用區域性變數來獲取處理結果。這種方式麻煩,但是權威。下面一段文字來自Sun公司對多執行緒模型的討論(《多執行緒程式設計指南》• 2006年10月,P231, 所有權歸Sun公司所有):

引用 —— 《多執行緒程式設計指南》• 2006年10月,P231, 所有權歸Sun公司所有

示例9–2 gethostbyname() 問題:

struct hostent *gethostbyname(char *name) {

static struct hostent result;
/* Lookup name in hosts database */
/* Put answer in result */
return(&result);
}
通常情況下,使用返回到區域性變數的指標不是一個好辦法。在本示例中使用指標有
效,是因為變數是靜態的。但是,當兩個執行緒同時使用不同的計算機名稱呼叫此變數
時,使用靜態儲存會發生衝突。
與errno 問題一樣,可以使用執行緒特定資料來替換靜態儲存。但是,此替換涉及到動態
分配儲存,並且會增加呼叫開支。
處理該問題的更好方法是使gethostbyname() 的呼叫方為呼叫結果提供儲存。呼叫方可
通過例程的其他輸出引數來提供儲存。其他輸出引數需要gethostbyname() 函式的新接
口。
線上程中常使用此技術來解決許多問題。在大多數情況下,新介面的名稱就是原有名
稱附加"_r",如gethostbyname_r(3NSL)。