当您在现有变量上调用malloc时会发生什么?

7
如果一个变量之前已经被malloc分配过内存,如果你再次调用malloc而不是realloc,会发生什么?这会导致内存泄漏吗?
我想知道这个问题的答案,以便在对函数参数进行malloc调用之前添加检查,以避免潜在的内存泄漏。
void memtest(char **foo)
{
        if ( foo )
                *foo = malloc(10); // Assuming *foo was already allocated in the past.
}

我应该总是做以下操作吗?
void memtest(char **foo)
{
        if ( foo ) {
                if ( *foo ) {
                        free(*foo);

                        *foo = NULL;
                }

                *foo = malloc(10);
        }
}

如果您添加一段代码片段,问题将更好。 - haccks
1
不存在“在变量上调用malloc”的情况。 - user253751
5个回答

7

“在变量上调用 malloc ”这样的说法是不存在的。

malloc会找到一些“空闲”内存,标记为“已使用”,并返回刚找到的内存起始地址。free会将一些“已使用”的内存标记为“空闲”。

当您运行以下命令时:

*foo = malloc(10);

以下是发生的事情:
  • malloc 找到了 10 个字节的空闲内存。
  • malloc 将这 10 个字节标记为已使用。
  • malloc 返回刚找到的 10 个字节的起始地址。
  • 您的程序 查看 foo 的值,因为 foo 是一个指针,所以它是一个地址。
  • 您的程序malloc 返回的地址存储在存储在 foo 中的地址中。

malloc 并不关心您的程序对其返回的地址做了什么。它不关心您的程序是否将其存储在简单变量、数组、另一个 malloc 的空间中,甚至是写入文件。它甚至不关心您的程序是否忘记了该地址,但您应该关心,因为如果您的程序不知道要释放的内存的地址,它将永远无法调用 free

有了这些知识,您应该能够看出此代码的作用:

char *bar;
bar = malloc(10);
bar = malloc(10);
free(bar);

请您耐心思考,然后阅读以下内容:
声明了一个名为“bar”的变量,其类型为“char *”。
程序调用了“malloc”函数。
“malloc”函数找到10个自由字节并将它们标记为已使用。
“malloc”函数返回10个字节开始的地址。
程序将地址存储在“bar”中。(现在,“bar”包含了前10个字节的地址)
程序再次调用“malloc”函数。
“malloc”函数找到另外10个自由字节并将它们标记为已使用。
“malloc”函数返回10个字节开始的地址。
程序将地址存储在“bar”中。(现在,“bar”包含了第二组10个字节的地址)
程序调用“free”函数,并传递第二组10个字节的起始地址。
“free”函数将第二组10个字节标记为自由。
这是内存泄漏。第一组10个字节永远不会被释放。我可以确定它们永远不会被释放,因为程序不知道它们所在的地址。
那么这个程序怎么样呢?
char *bar;
char *baz;
bar = malloc(10);

baz = bar;
baz = malloc(10);

在这里,我已经对一个已经持有malloc的地址的变量进行了"调用"。那么这是一种内存泄漏吗?单独来看不是。程序可能仍然可以free第一个内存块,其地址仍然存储在baz中。

但是,这绝对是一种内存泄漏:

void func()
{
    char *bar;
    char *baz;
    bar = malloc(10);

    baz = bar;
    baz = malloc(10);
}

我甚至没有改变代码,只是将其放在一个函数中,现在它成为了一个内存泄漏!Whaaaaaaaaa?

要记住什么是内存泄漏。当程序分配内存但从未释放时,就会出现内存泄漏。你不能仅通过查看malloc调用来确定程序是否释放了所有内存。


至于第二部分 - “我应该这样做吗?”

不应该这样做。

只有在调用方忘记free并且地址传递给您的函数的旧值是malloc指针时,您的建议才能生效。

您的建议将破坏以下任何一个函数:

void func1()
{
    char c;
    char *ptr = &c;
    memtest(&ptr); // Tries to free something that wasn't malloced!
    // ... do something with ptr ...
}
void func2()
{
    char *ptr;
    memtest(&ptr); // Passes a garbage address to free!
    // ... do something with ptr ...
}
void func3()
{
    char *ptr1 = malloc(5)
    char *ptr2 = ptr1;
    memtest(&ptr1);
    // ... do something with ptr1 and ptr2 ...
    free(ptr1); // Frees memory that was already freed!
}

总之:标题中的问题没有意义,检测内存泄漏也不是那么简单。你所尝试做的事情是一个不好的想法。

我觉得只是因为我写了一个长答案,就自动获得了被接受的答案。它真的有帮助吗? - user253751
我也这么想。你因为辛勤工作而获得了奖励 :) - haccks
是的,更多的编写速度更快是被接受的答案...哈哈 ;) 顺便说一句,好答案。 - LPs

5

如果*foo的指针包含了先前分配的内存唯一的指针,则会泄漏先前存在的内存。否则,这是完全无害的。


5

如果有问题,调用malloc是不相关的。

问题在于赋值。当你执行x=42;后,x的先前值已经消失了,无论它可能有多重要。同样地,如果foo是您拥有的唯一指向某些内存的指针,如果您覆盖foo,则您将不再能够访问该内存。

您不能调用free而不指定要释放什么。因此,如果在失去对其唯一指针之前没有释放内存,则永远无法释放它,这就是内存泄漏的定义。


3
由于以前分配的内存没有被任何变量引用,因此会导致内存泄漏并且你失去了其地址。
在这方面没有特定的检查。您可以在声明时将指针设置为NULL,并在每次释放内存时将其设置为NULL。之后,您可以在调用malloc之前检查if (pointer == NULL)
使用realloc可以避免内存泄漏的问题,因为如果指针是空指针,则realloc函数的行为类似于指定大小的malloc函数。但是如果您的指针未声明设置为NULL,可能会导致问题。
例如:
int *foo = NULL;

void bar( void )
{
     foo = realloc(foo, 10*sizeof(int));
}

一个好的或者不好的realloc特性是它会保留你内存中的内容。

我应该总是这样做吗?

void memtest(char **foo)
{
        if ( *foo )
                free(*foo);

        *foo = malloc(10);
}

不,你不应该这样做,因为free不会将指针设置为NULL,如果你的指针仍然具有已经free的内存的值,程序会出现错误。 我的意思是,在代码的某个地方free指针而没有重新分配它,这样的代码容易出错。 你必须确保指针被声明为NULL作为初始值。

еңЁжҲ‘дёӘдәәзңӢжқҘпјҢзј–еҶҷжӣҙжҳ“иҜ»зҡ„д»Јз ҒйқһеёёйҮҚиҰҒгҖӮжҲ‘зҡ„ж„ҸжҖқжҳҜеҸӘжңүеңЁйҮҚж–°еҲҶй…Қд№ӢеүҚдҪҝз”Ёmalloc并且йңҖиҰҒдҝқз•ҷеҶ…еӯҳеҶ…е®№ж—¶жүҚдҪҝз”ЁreallocгҖӮдҪҶжҳҜжӮЁеҸҜд»ҘеңЁж•ҙдёӘд»Јз Ғдёӯд»…дҪҝз”ЁдёҖж¬ЎfreeжқҘдҪҝз”ЁreallocгҖӮ - LPs

1
当你在现有变量上调用malloc时会发生什么?
有几种可能性:
  • 如果之前的malloc成功分配了内存,则重新分配内存将导致内存泄漏。
  • 如果之前的malloc调用未能成功分配内存,则不会发生内存泄漏。

Should I always do the following?

void memtest(char **foo)
{
        if ( *foo )
                free(*foo); 
        *foo = malloc(10);
} 

是的。通过这种方式,您可以确保没有内存泄漏。


free不会将指针设置为NULL,所以if (*foo) free(*foo);是错误的。我有什么遗漏吗? - LPs
@LPs; if (*foo) 是检查前一次调用 malloc 是否成功。如果不成功,malloc 必须返回 NULL。因此,如果 *foo 没有指向 NULL,则释放分配的块。请注意,在检查 *foo 是否为 NULL 后,才释放 *foo - haccks
我的意思是,如果在代码的其他部分内存已被释放且未分配,则此类函数将失败。 - LPs
@LPs;我想我误解了你的评论。看,*foo = malloc(10)将分配所请求的内存并使*foo指向分配块的第一个块,或者它将返回NULL,该值将被分配给*foo。如果已释放分配的内存,则最好将NULL分配给指向该内存的指针。 - haccks
是的,这正是我想表达的意思。嗯,这是代码维护的最佳实践问题。如果程序员不需要重新分配内存,就可以释放内存而不重新分配它。 :) - LPs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接