如何正确释放使用malloc()分配的结构体?

6

我有一个使用malloc()分配的结构体,在使用完之后,我想要free()它,但是我的程序在这里卡住了。有人能告诉我我做错了什么吗?

以下是我的代码:

struct data  
{  
char *filename;  
char *size;  
};   
 //primarypcs is a long type variable
struct data *primary = (struct data *)malloc( primarypcs * sizeof( struct data ) );  
memset( primary, 0, sizeof(struct data *) * primarypcs );  
...
...
...
for ( i = 0; i < primarypcs; i++ )  
{
   free( primary[i].filename );  //<----my program freezes here
   free( primary[i].size );      //<----or here
}
free( primary );  

提前感谢!

kampi

编辑:

我应该如何正确地为文件名和大小分配内存?

编辑2:

抱歉,我很匆忙,没有告诉你需要的所有信息。现在让我说吧 :) 基本上,我想创建一个应用程序,它可以获取两个给定驱动器/文件夹的文件列表,然后进行比较。我认为(并且仍然认为),最简单的方法是将文件名及其大小存储在像上面提到的结构中。因此,我必须为文件名和大小以及结构动态分配内存(我认为这就是所谓的动态分配内存)。


2
我没有看到你为 filenamesize 分配内存。 - Prasoon Saurav
2
是的,你的代码有一个bug:在malloc中你使用了sizeof(struct data),这似乎是正确的,但在memset中你使用了sizeof(struct data*),它只有一半的大小。这意味着你只清零了一半的内存,当你尝试释放它时,最后1/2的内存将会出错。答案应该是free(primary);,仅此而已。 - cmroanirgo
如果这是C ++,你应该使用new和delete。 - Xorlev
@Xorlev:我已经在我的答案中提到了。请看下面我的回答。 - Prasoon Saurav
你需要选择C++或C作为语言。你的示例是用C而不是C ++编写的。 - Potatoswatter
根据您的第二次编辑,您希望 size 是一个整数类型,比如 size_t,而不是 char *。请参阅我的答案以获取详细信息。 - Alok Singhal
8个回答

7

您没有呈现完整的代码,很多事情可能会出错,但已经有一个错误是显而易见的。该行代码为

memset( primary, 0, sizeof(struct data *) * primarypcs );   

它并没有做你想象中的事情。由于sizeof中的类型错误,它并没有将整个数组清零。最有可能的情况是应该写成:

memset( primary, 0, sizeof(struct data) * primarypcs );   

注意在 sizeof 下面没有 *。由于这个错误,数组中的大多数指针都包含垃圾值作为它们的初始值。如果您没有在省略的代码中将它们设置为有意义的值,则对 free 的调用将接收到垃圾参数并失败。
一般来说,为了减少此类错误的可能性,最好避免在程序中提及类型名称,除非在声明中。由于您的问题被标记为 C++(尽管它看起来像 C),因此不可能消除对 malloc 的类型转换,但除此之外,我认为以下代码看起来更好。
struct data *primary = (struct data *) malloc( primarypcs * sizeof *primary );   
memset( primary, 0, primarypcs * sizeof *primary );   

另外,如果您的代码是用于C++,则可以以更优雅、简洁和便携的方式获得相同的结果

data *primary = new data[primarypcs]();

当然在这种情况下,你需要使用适当的C ++功能来释放内存,而不是使用free


嗨!感谢您的帮助。这是我犯的一个错误,但是在根据您的答案纠正我的代码后,我的程序仍然卡住了。后来我意识到我错误地输入了一个变量,并且我只为5个项目分配了内存,但是我插入了超过5个项目到结构中。再次感谢你! - kampi

3
结构体中的字符串是如何分配的?如果它们被静态分配为常量,则不要使用这种方式释放它们,只需要free(primary);。释放未使用malloc分配的内容会使堆管理器发生错误。
如果字符串指针是通过malloc()或calloc()设置的,则这是正确的方法。

2

如果您正在使用C++进行此操作,您(几乎肯定)不应使用像这样的内容:

data *primary = new data[primarypcs]();

相反,你应该使用像这样的东西:

struct data {
    std::string filename;
    std::string size;
};

std::vector<data> primary(primarypcs);

在这种情况下,您可以更简单地处理内存管理:在需要的范围内定义向量,当它超出范围时,内存将自动释放。
在C++中使用数组new(例如new x[y])是您最好避免的。曾经有一段时间(大约15年前),它几乎是唯一可用的工具,因此几乎不可避免地使用它 - 但那天已经过去了,而且自从10年前以来,真正有好理由使用它的日子已经过去了。
由于“除了实现类似于vector之类的东西”,必然会有评论,我要指出,即使在实现vector时,也不使用数组new - 您(通过分配器间接地)使用::operator new来分配原始内存,在该内存中创建对象,并使用显式的dtor调用来销毁对象。

1

正如其他人所说,你展示的代码片段中有两个明显的错误:

  1. 你没有为刚刚分配的结构体的filenamesize成员分配内存,
  2. 你的memset()调用使用了错误的大小。

你的memset()调用可以通过简化和更正来实现:

memset(primary, 0, primarypcs * sizeof *primary);

你的代码还存在另一个微妙的问题:C标准并不保证全零比特是空指针常量(即NULL),因此memset()不是将指针设置为NULL的正确方法。实现你想要的功能的可移植方式是:

size_t i;
for (i=0; i < primarypcs; ++i) {
    primary[i].filename = NULL;
    primary[i].size = NULL;
}

filenamesize分配内存,这取决于您的需求。假设您确定filename需要n字节,而size需要m字节。那么,您的循环将变成以下内容:
size_t i;
for (i=0; i < primarypcs; ++i) {
    size_t n, m;
    /* get the values of n and m */
    primary[i].filename = malloc(n * sizeof *primary[i].filename);
    primary[i].size = malloc(m * sizeof *primary[i].size);
}

如果你想的话,可以从上面省略与sizeof *primary[i].filenamesizeof *primary[i].size的乘法:C保证sizeof(char)为1。我写上面是为了完整性和当filenamesize更改类型时的情况。

此外,请注意,如果filename是长度为k的字符串,则需要(k+1)个字节,因为有终止符0(所以n == k+1)。

如果我猜测的话,你想让size存储相应filename的长度?如果是这样的话,size不应该是char *而应该是size_t。但由于我不知道你计划如何使用filenamesize,所以我不确定。

一定要检查malloc()的返回值。它会在失败时返回NULL。我从上面的代码中省略了检查以简化代码。

你的帖子也被标记为C++,所以如果你愿意使用C++,那么也有一个C++的解决方案可用。


你好! 我应该如何正确地为文件名和大小分配内存? - kampi
嗨!size是char*类型的是有原因的。我用long类型来获取文件的大小,但后来我必须将其作为char类型使用,所以我将它从int转换为char,并将其存储在结构体中。 - kampi

0

这是因为您没有显式分配filenamesize的内存。因此,尝试执行free(primary[i].filename);free(primary[i].size);将会引发未定义行为

只需要free(primary)就足够了。

编辑:

此问题已被标记为C++。因此,C++的方法是使用new而不是malloc来定义用户类型。

有关newmalloc之间的区别,请参见this.

C++中,您只需要编写

 data *primary = new data[primarypcs](); //() for value initialization

假设他们单独分配了文件名,那么样例代码可以被视为可行的。然而,一个已经进行了malloc的“size”看起来很可疑,使用size_t可能更合适(除非它真的是一个char字符串)。 - cmroanirgo

0

您未能使用memset函数清空整个数组,导致释放了一个垃圾内存指针。建议使用calloc函数代替malloc/memset以避免这种错误:

struct data *primary = calloc(primarypcs, sizeof(struct data));

这将分配并清除内存。 如果您还想初始化所有的struct data条目:

for (i = 0; i < primarypcs; ++i) {
    primary[i].filename = malloc(...);
    primary[i].size = malloc(...);
}

(您没有描述文件名和大小,因此我将...留给您填写)。


0
将代码底部的 for 循环替换为 free (primary); 应该可以解决问题。

0

这将解决你在memset中遇到的问题,这给你带来了各种问题。

memset( primary, 0, sizeof(struct data) * primarypcs );  

简而言之,您在“primary”结构的末尾留下了未初始化的内存。

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