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

发布于 2024-09-19 23:41:58 字数 831 浏览 6 评论 0原文

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

I'm curious what the proper way to trim a string is to ensure that no memory leak occurs. I guess this may really be a question based on exactly how free() works. I've included the code for my trim() function. See below.

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧人哭 2024-09-26 23:42:05

这实际上并不是免费工作原理的答案,但我会按照以下方式做一些事情:

char * trim_realloc(char * str) {
字符 * p = str;
字符 * e;
字符*ne; // 新的结束
字符 * r;
size_t 长度;

// 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猫”
“猫猫”
“猫在”
“猫”
“猫”

在开始修剪尾巴之前

。现在,回到 free 的工作原理——free 需要传递 NULL 或堆分配函数之一传递给您的值。实现了一些堆分配库,以便当 malloc 分配数据时,该数据块的大小存储在 malloc 返回的地址之前的字节中,并且当您调用 free 时,该指针前面的字节用于确定要执行的操作。该内存块的实际大小是。如果您传递了 malloc(或 calloc、realloc 或类似的)未返回的内容,则 free 可能会查找错误的位置并使用它在那里找到的任何内容作为您要释放的块的大小 - 并且不会有任何好处的这个。

This isn't really an answer to how free works, but I would do something along these lines:

char * trim_realloc(char * str) {
char * p = str;
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;
}

}

You should note my comment on the line with realloc Since I zeroed out trailing space anyway (and since many realloc implementations only worry about "is it big enough", not "is there too much extra space") you could have just let the buffer that your string lived in take up too much space at the end. It's still \0 terminated at the correct spot (unless there's bugs in my untested code, which there could be).

Other things you could do would be to just move the string to the beginning of the buffer and then trim the tail, so that:

"  cat   "

went through the steps:

"c cat "
"ca cat "
"catcat "
"cat at "
"cat t "
"cat "

before you started trimming the tail.

Now, back to how free works -- free needs to be passed either NULL or a value that one of the heap allocation functions passed to you. Some heap allocation libraries are implemented so that when malloc allocates data the size of that data chunk is stored in the bytes just before the address that malloc returns, and when you call free the bytes just in front of that pointer are used to determine what the size of that memory chunk actually is. If you pass the something that was not returned by malloc (or calloc, or realloc, or similar) then free may look in the wrong place and use whatever it finds there as the size of the chunk you are freeing -- and nothing good comes of this.

黑白记忆 2024-09-26 23:42:05

您不需要额外的 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;
}

不快但安全,对于您的示例, free 永远不会失败,因为 s 没有改变。只有内容可以更改。

You don't need an extra malloc/realloc/... in trim, like:

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;
}

Not fast but safe, free fails never for your examples, because s not changed. Only the s content can change.

一张白纸 2024-09-26 23:42:04

是的,这会导致内存泄漏,但更糟糕的是,它会导致未定义的行为。由于 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);

Yes, this will cause a memory leak, but worse, it causes undefined behavior. Since trim modifies the pointer variables, main passes a pointer to free that was not returned by malloc. This is undefined behavior, and it will corrupt the heap on many implementations.

There are at least three correct ways to handle this.

1. Have trim allocate and return a new string, and make the caller responsible for freeing the new one, as well as the old (if needed):

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

2. Let the caller allocate a new string the same length (to be conservative), and pass both pointers in.

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

3. Trim the string in place, shifting it left:

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

int *trim(char *orig);
trim(testStr1);
// ...
free(testStr1);
倒带 2024-09-26 23:42:03

您传递给free的指针需要完全与您从malloc(或calloc或< code>realloc),而不仅仅是指向 malloc 返回的内存区域的指针。因此,您的第二根字符串是导致问题的原因。您的第一个和第三个都很好,因为您传递给 free 的指针与您从 malloc 收到的指针(通过 strdup)相匹配。

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

The pointer you pass to free needs to be exactly the same pointer you received from malloc (or calloc or realloc), not just a pointer into the region of memory that malloc returned. As such, your second string is the one that causes a problem. Your first and third are fine because the pointer you pass to free matches the one you received from malloc (via strdup).

What you're getting in that case, however, isn't really a memory leak -- it's undefined behavior.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文