2010-09-20 34 views
1

我很好奇修剪字符串的正確方法是確保不會發生內存泄漏。我猜這可能真的是一個基於free()工作原理的問題。我已經包含了我的trim()函數的代碼。見下文。字符串修剪會導致內存泄漏?

int main() 
{ 
    char* testStr1 = strdup("some string"); 
    char* testStr2 = strdup(" some string"); 
    char* testStr3 = strdup("some string  "); 

    trim(&testStr1); 
    trim(&testStr2); 
    trim(&testStr3); 

    free(testStr1); // no memory leak 
    free(testStr2); // possible memory leak? 
    free(testStr3); // possible memory leak? 

    return 0; 
} 

int trim(char** pStr) 
{ 
if(pStr == NULL || *pStr == NULL) 
    return FAILURE; 
char* str = *pStr; 
while(isspace(*str)) { 
    (*pStr)++; 
    str++; 
} 

if(*str == 0) { 
    *pStr = str; 
    return SUCCESS; 
} 

char *end = str + strlen(str) - 1; 
while(end > str && isspace(*end)) 
    end--; 
*(end+1) = 0; 

*pStr = str; 
return SUCCESS; 
} 

回答

5

是的,這會導致內存泄漏,但更糟的是,它會導致未定義的行爲。由於trim修改指針變量,因此main傳遞指向free的指針,該指針未由malloc返回。這是未定義的行爲,它會破壞許多實現上的堆。

至少有三種正確的方法來處理這個問題。

1.  有修剪分配並返回一個新的字符串,使主叫方負責釋放新的,以及舊的(如果需要):

char *trim(char *orig); 
// ... 
char *trimmed1 = trim(testStr1); 
free(testStr1); 
// ... 
free(trimmed1); 

2.  讓呼叫者分配一個新的字符串相同的長度(保守),並在通過兩個指針

int trim(char *orig, char *new); 
// ... 
char *trimmed1 = malloc(strlen(testStr1) + 1); 
trim(testStr1, trimmed1); 
free(testStr1); 
// ... 
free(trimmed1); 

  3.修剪到位串,移位它留下:

| | |t|r|im| | |\0|-> 
|t|r|i|m|\0| 

int *trim(char *orig); 
trim(testStr1); 
// ... 
free(testStr1); 
+0

。指定的函數處理客戶端代碼的問題,而不是引入新的雜技。 – grossvogel 2010-09-20 23:58:17

+2

值得注意的是'memmove()'對實現選項#3很有用。 – caf 2010-09-21 00:19:52

14

傳遞給free需求的指針是正是malloc(或callocrealloc)收到相同的指針,不僅是一個指針指向的內存區域是malloc返回。因此,你的第二個字符串是導致問題的字符串。您的第一個和第三個都沒問題,因爲您傳遞給free的指針與您從malloc(通過strdup)收到的指針相符。

然而,你在這種情況下得到的不是內存泄漏 - 它是未定義的行爲。

+0

+1。通常你會得到堆腐敗,這使得關於內存泄漏的所有問題都有些誇張。一個相關的C + +問題:#3的#1的+1的http://stackoverflow.com/questions/1913343/how-could-pairing-new-with-delete-possibly-lead-to-memory-leak-only – sharptooth 2010-09-21 10:36:07

0

這是不是一個真正的答案,如何自由的作品,但我會做這些方針的東西:

的char * trim_realloc(字符* STR){ 的char * p =海峽; char * e; char * ne; // new end char * r; size_t len;

// Since you put this level of error testing in your program 
if (!str) { 
    return str; // str is NULL 
} 

while (*p || isspace(*p)) { 
    p++; 
} 

len = strlen(p); 
e = p + len; 

ne = e; 

while (ne > p) { 
    if (isspace(*ne)) { 
     *ne = 0; 
     ne--; 
    } else { 
     break; 
    } 
} 


if (p == str) { 
    if (e != ne) { 
     return realloc(str, len+1); // only tail trim -- you could just return str here 
    } else { 
     return str; // no actual trim 
    } 
} else { 
    r = strdup(p); 
    free(str); // str is the head of the string, so that's what we have to free 
    return r; 
} 

}

你應該注意到我與realloc行註釋因爲我歸零反正尾隨空格(因爲許多realloc的實現只擔心「是不是足夠大」,而不是「有沒有過很多額外的空間「),你可以讓你的字符串所在的緩衝區最後佔用太多空間。它仍然在正確的位置終止(除非我的未經測試的代碼中存在錯誤,這可能存在錯誤)。

你可以做其他的事情是隻串移動到緩衝區的開頭,然後修剪尾巴,使:

「C貓:

" cat " 

一步步去「 」CA貓「 」catcat「 」貓在「 」貓T「 」貓「

,然後開始修剪尾巴。

現在,回到免費工作 - 免費需要傳遞NULL或一個堆分配函數傳遞給您的值。一些堆分配庫被實現,以便當malloc分配數據時,該數據塊的大小存儲在malloc返回的地址之前的字節中,並且當您調用free時,該指針前面的字節用於確定該內存塊的大小實際上是。如果你傳遞的不是malloc(或者calloc,或者realloc或者類似的)返回的東西,那麼free可能會在錯誤的地方查找,並使用它在那裏找到的任何東西作爲你正在釋放的塊的大小 - 並且沒有什麼好處這個的。

0

你並不需要一個額外的malloc/realloc的/ ......在裝飾,如:

char *trim(char *s) 
{ 
    while(isspace(*s)) 
    memmove(s, s+1, strlen(s)); 
    while(*s && isspace(s[strlen(s)-1])) 
    s[strlen(s)-1] = 0; 
    return s; 
} 

還不快,但安全,自由從來沒有失敗爲你的例子,因爲不是改變。只有s 內容可以更改。