2

我今天写了下面的代码来试验返回分配内存的指针。该程序运行良好,但我确实有几个问题:

  1. rmch我使用的返回值分配内存realloc。我了解该功能以及它在大多数情况下的作用我只是不太确定我理解的将分配的内存大小调整为#c 字符(char *)的那一行的意义是什么,但该部分有什么作用?ret = (char *) realloc(ret, c * sizeof(char));realloc(ret, c * sizeof(char));(char *)

  2. 我不释放ret指向任何地方的分配内存,但我释放了一个ret在我的main函数中调用的指针。分配的内存发生了什么?如果它没有被释放,我将如何去释放它?

代码

#include <stdio.h>
#include <stdlib.h>
char* rmch(char *str, char ch)
{
    char *ret = NULL;
    int c = 0, i;

    for(i = 0; str[i] != '\0'; i++)
    {
        if(str[i] != ch)
        {
            c++;
            ret = (char *) realloc(ret, c * sizeof(char));
            ret[c - 1] = str[i];
        }
    }
    ret[c] = '\0';
    return ret;
}

int main(void)
{
    char *foo = rmch("f o o", ' ');

    printf("%s", foo);
    free(foo);
    return 0;
}
4

3 回答 3

2

在 main 中释放 Foo 将释放您在 rmch 中重新分配的 ret。

原因是 free() 转到由 rmch 返回的指针指定的地址。

此外,由于您使用“C”标签标记了这篇文章,因此您永远不应该转换分配的返回值。void *'s只要在分配期间为它们提供了正确的字节大小,它们就会自动、隐式地提升为它们存储的任何内容。

顺便说一句,您永远不应该将任何分配的内存直接存储到您正在使用/将要使用的指针中,因为如果返回的指针为 NULL 并且您仍然指向旧内存,这可能会导致内存泄漏。

最好这样做:

temp = realloc();
if( temp == NULL)
{
     printf("realloc failed to reallocated memory!");
     return NULL;
}
ret = temp;

然后您还需要检查 main 中的 NULL 返回。

于 2012-08-02T19:31:39.270 回答
1

你的代码还有一个问题,当你用 valgrind 运行它时,你会得到这样的东西:

toc@UnixServer:~$valgrind --leak-check=full --show-reachable=yes ./realloc_pb
==17077== Memcheck, a memory error detector
==17077== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==17077== Using Valgrind-3.6.1-Debian and LibVEX; rerun with -h for copyright info
==17077== Command: ./realloc_pb
==17077== 
==17077== Invalid write of size 1
==17077==    at 0x80484BF: rmch (realloc_pb.c:19)
==17077==    by 0x80484E3: main (realloc_pb.c:25)
==17077==  Address 0x41b709b is 0 bytes after a block of size 3 alloc'd
==17077==    at 0x402896C: realloc (vg_replace_malloc.c:525)
==17077==    by 0x804848A: rmch (realloc_pb.c:14)
==17077==    by 0x80484E3: main (realloc_pb.c:25)
==17077== 
==17077== Invalid read of size 1
==17077==    at 0x402903D: __GI_strlen (mc_replace_strmem.c:284)
==17077==    by 0x4098739: puts (ioputs.c:37)
==17077==    by 0x4050112: (below main) (libc-start.c:226)
==17077==  Address 0x41b709b is 0 bytes after a block of size 3 alloc'd
==17077==    at 0x402896C: realloc (vg_replace_malloc.c:525)
==17077==    by 0x804848A: rmch (realloc_pb.c:14)
==17077==    by 0x80484E3: main (realloc_pb.c:25)
==17077== 
foo
==17077== 
==17077== HEAP SUMMARY:
==17077==     in use at exit: 0 bytes in 0 blocks
==17077==   total heap usage: 3 allocs, 3 frees, 6 bytes allocated
==17077== 
==17077== All heap blocks were freed -- no leaks are possible
==17077== 
==17077== For counts of detected and suppressed errors, rerun with: -v
==17077== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 11 from 6)

因为您需要为空终止字符分配更多空间,所以也修改它:

temp = (char *) realloc(ret, (c + 1) * sizeof(char)); // It's better to use temp pointer as suggested by ardent sonata

最后一件事是关于强制转换的 malloc 或 realloc 函数,它并不是真正必要的,它对编译器很有用,以防你忘记添加正确的头文件(stdlib.h),所以他可以警告你(http://c-faq. com/malloc/mallocnocast.html)。

希望这有帮助。

问候。

于 2012-08-02T20:55:06.090 回答
0

malloccalloc将为您提到的大小分配内存。realloc将调整已分配内存的大小,它可以缩小或扩展现有的堆缓冲区。

例如,如果我们将现有的堆缓冲区从n字节扩展到n+1字节,则意味着我们必须调用 realloc 之类的realloc(existing_bufPtr, n+1)。现在操作系统必须将现有缓冲区增加一个字节,因此它将检查第一个n+1字节existing_bufPtr是否未分配给任何其他用途。如果是这样,它只会将现有缓冲区的大小扩大到n+1,否则它将在单独的位置分配新缓冲区,并将值从现有缓冲区移动到新缓冲区。然后它将返回新分配的缓冲区的起始地址。

所以我们必须检查返回地址是否与我们传递的地址相同,如果它不一样,我们必须释放前一个缓冲区。所以改变你的代码如下

if(str[i] != ch)
{
   c++;
   ret_new = (char *) realloc(ret, c * sizeof(char));
   if (NULL == ret_new)     
   {
       return NULL;
   }
   else if (ret_new != ret)
   {
       free(ret);
       ret = ret_new;
   }

   ret[c - 1] = str[i];
}

使用缩小现有堆缓冲区realloc不会返回新地址。但扩展可能会返回新地址。

realloc将返回指针类型void *,我们必须将它类型化为我们的指针类型char *

内存分配函数 ( malloc,callocrealloc) 对于操作系统来说始终是一项代价高昂的操作。因此,与其调用realloc时间n,不如只使用一次分配内存malloc(sizeof(str)),这样性能会很好。

于 2012-08-03T04:42:31.797 回答