2010-09-20 45 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 内容可以更改。