字符串修剪导致内存泄漏?

时间:2010-09-20 23:18:42

标签: c string memory-leaks trim

我很好奇修剪字符串的正确方法是确保不会发生内存泄漏。我想这可能真的是一个基于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;
}

4 个答案:

答案 0 :(得分:14)

您传递给free的指针需要与<{1}}(或malloccalloc)收到的指针完全相同,不只是指向realloc返回的内存区域的指针。因此,您的第二个字符串是导致问题的字符串。您的第一个和第三个是正常的,因为您传递到malloc的指针与您从free(通过malloc)收到的指针相匹配。

然而,在这种情况下你得到的并不是内存泄漏 - 它是未定义的行为。

答案 1 :(得分: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);

答案 2 :(得分:0)

这不是一个真正的免费工作的答案,但我会按照以下方式做点什么:

char * trim_realloc(char * str){     char * p = str;     char * e;     char * ne; //新结局     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实现只担心“它是否足够大”,而不是“是否有太多的额外空间”)你可以让你的字符串所在的缓冲区占用太多空间。它仍然是\ 0在正确的位置终止(除非我的未经测试的代码存在错误,可能存在)。

你可以做的其他事情就是将字符串移动到缓冲区的开头,然后修剪尾部,以便:

"  cat   "

完成了以下步骤:

“c cat”    “ca cat”    “catcat”    “猫在”    “猫咪”    “猫”

在开始修剪尾巴之前。

现在,回到免费的工作方式 - free需要传递NULL或者传递给你的一个堆分配函数的值。实现了一些堆分配库,以便当malloc分配数据时,该数据块的大小存储在malloc返回的地址之前的字节中,当您调用free时,该指针前面的字节用于确定该内存块的大小实际上是。如果你传递的东西不是由malloc(或calloc,或realloc或类似的东西)返回的,那么free可能会在错误的地方查找并使用它在那里找到的任何东西作为你正在释放的块的大小 - 没有什么好处来这个。

答案 3 :(得分: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 内容才能更改。