函数中的malloc()结构,结束程序前的free()

时间:2012-10-21 23:33:11

标签: c pointers malloc valgrind

我正在学习C的基础知识,现在正在使用malloc()。假设我有一个函数要求用户输入,填充带有所述数据的结构,并保存在数组中的结构(全部通过主函数的引用传递)。

我通过Valgrind运行程序,我得到“仍然可以访问”的字节(这应该没问题?不算漏洞),但是保存的任何数据都会丢失。

程序运行完成后如何释放内存?此外,我在代码中有一些(2)问题只是为了澄清一些事情,如果有人可以向我解释,我会很感激。

以下是一些类似于我想要做的代码:

我声明了以下结构:

struct Person {
  char name[MAX_INPUT];
  int age;
};

我正在写一个像这样的函数:

int function2(struct Person *list, int *index) {
  struct Person *prsn = malloc(sizeof(struct Person)); 
  // !Why do we sometimes cast the malloc or not? 
  // I sometimes get errors when I do, sometimes when I don't, 
  // while the surrounding code is pretty much the same.
  assert(prsn != NULL);

  // User input code goes here ... 

  // Now to save the Person created
  strcpy(prsn->name, nameInput);
  prsn->age = ageInput;
  list[(*index)++] = *prsn; 
  // !Why use the dereferencing *prsn here? 
  // why not directly prsn? Or is that saving the memory address and not very useful.

  return 0;
}

这是我的主要功能:

int main(int argc, char *argv[]) { 
  struct Person personList[MAX_SIZE];
  int index;

  function2(personList, &index);

  // Before closing, I want to free any mallocs I have done here. free()

  return 0;
}

Valgrind报告:

LEAK SUMMARY:
==1766==    definitely lost: 44 bytes in 1 blocks
==1766==    indirectly lost: 0 bytes in 0 blocks
==1766==      possibly lost: 0 bytes in 0 blocks
==1766==    still reachable: 10,355 bytes in 34 blocks
==1766==         suppressed: 0 bytes in 0 blocks

提前谢谢。

编辑:修复了function2参数,返回等事情。我道歉,正在快速写下来说明我关于释放记忆的主要问题。感谢您的更正提示,但真正的代码实际上是正确编译的。

Edit2:在main之后添加一个简单的循环,建议使用free(),我得到以下错误。

==2216== LEAK SUMMARY:
==2216==    definitely lost: 44 bytes in 1 blocks
==2216==    indirectly lost: 0 bytes in 0 blocks
==2216==      possibly lost: 0 bytes in 0 blocks
==2216==    still reachable: 10,355 bytes in 34 blocks
==2216==         suppressed: 0 bytes in 0 blocks
==2216== 
==2216== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==2216== 
==2216== 1 errors in context 1 of 2:
==2216== Invalid free() / delete / delete[] / realloc()
==2216==    at 0x563A: free (in /usr/local/Cellar/valgrind/3.8.1/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==2216==    by 0x10000194E: main (in ./test.out)
==2216==  Address 0x7fff5fbf9dd0 is on thread 1's stack
==2216== 
==2216== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

3 个答案:

答案 0 :(得分:6)

代码分析

让我们一次解剖一下:

int function2(struct Person *list) {

Iserni中注明answer,此定义与您对函数的调用不一致。我一般同意他对现有代码的更正(但我会建议不久修改它):

int function2(struct Person *list, int *index)


  struct Person *prsn = malloc(sizeof(struct Person)); 
  // !Why do we sometimes cast the malloc or not? 
  // I sometimes get errors when I do, sometimes when I don't, 
  // while the surrounding code is pretty much the same.

这取决于你是否使用C或C ++;在C ++中,你必须从malloc()转换返回值(如果你完全使用malloc();你通常不应该在C ++中)。在C中,演员阵容是可选的。有一种想法,省略演员表明插入演员可以隐藏的错误。我没有订阅;我相信malloc()应该通过<stdlib.h>声明,如果范围内没有声明,编译器应该发出警告,并且如果范围内有声明,则演员不能掩盖罪恶。另一个可能的问题是你指定一个非指针的指针;这也是编译器应该抱怨的错误。

  assert(prsn != NULL);

这通常不被认为是处理内存分配错误的明智的长期方法。

  // User input code goes here ... 

  // Now to save the Person created
  strcpy(prsn->name, nameInput);
  prsn->age = ageInput;
  list[(*index)++] = *prsn; 
  // !Why use the dereferencing *prsn here?

由于:

  • liststruct Person *
  • 因此list[i]struct Person(尽管您拼写i(*index)++)。
  • 因此,您必须为其指定struct Person
  • prsnstruct Person *
  • 因此*prsn也是struct Person
  • 因此,作业是正确的<#39;。
  • 它也会泄漏。
  • 您已使用list[i]的内容覆盖*prsn的内容。
  • 您没有将指针保存到任何地方的prsn
  • 当你从函数返回时,你会泄漏内存。

解决这个问题所需的手术是不可忽视的:

int function2(struct Person **item)
...
*item = prsn;

你必须修改电话;在解剖main()时,我会回过头来看。

  // why not directly prsn? Or is that saving the memory address and not very useful.
}

宣布您的函数返回int但您没有显示return。如果您不返回某个值,请将该函数声明为void,特别是如果您将main()中的代码忽略返回值。

您的上一条评论大部分都是上述讨论所涵盖的;保存内存地址对于阻止泄漏至关重要,因此非常有用。

  

这是我的主要功能:

int main(int argc, char *argv[]) { 
  struct Person personList[MAX_SIZE];
  int index;

使用未初始化的变量是个坏消息。它最多只是意外地归零。您无法将随机值用作数组索引;明确地初始化它。此外,我们需要一个指针数组,而不是一个结构数组:

  struct Person *personList[MAX_SIZE];
  int index = 0;

  ...other code...

  function2(personList, &index);

修改后的功能:

  function2(&personList[index++]);

这是可取的;这意味着function2()不需要知道数组;你只需要传递指定分配的内存指针的指针的地址。这会减少main()函数与function2()之间的耦合,从而使代码更加简单。

  // Before closing, I want to free any mallocs I have done here. free()

所以你写道:

  for (int i = 0; i < index; i++)
      free(personList[i]);

这将释放所有已分配的内存。

  return 0;
}

我希望在main()结束时看到明确的回报,即使C99说它不是100%必要的。

确保在编译时启用了足够的警告。如果您正在使用GCC,那么gcc -Wall应该是您运行的最低编译警告级别(当您这样做时,代码中应该没有警告)。我经常提出更严格的警告:gcc -std=c99 -Wall -Wextra -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes -Wshadow。您需要包含-O3(或某种程度的优化)才能获得一些警告。关于原型的东西反映了我使用的旧代码库的偏执狂,它仍然具有K&amp; R函数定义。


回答评论

  

第一个问题,当我查看您的详细信息并尝试解决问题时:结构数组和指针数组之间是否存在内存影响?

是的,但它可能不是你想的。如果使用结构数组,则不需要为结构分配内存动态,因为编译器已经为您分配了它们。由于练习的目的是使用指针和malloc(),因此最好使用指针。就空间而言,指针数组使用的总内存略多(但内存泄漏的数量会减少)。

  

我正在尝试更改我的代码以使用指针数组。但是现在使用function2(personList, &index);来调用function2会给我以下警告:incompatible pointer types passing 'struct Person *[512]' to parameter of type 'struct Person *'。如果我在主要问题中编写额外的代码以进入细节,这样可以吗?作为一个注释,我试图尽可能多地引用变量,以便暂时不让程序从函数复制数据到函数。

如果您没有进行所有更改,编译器是正确的。使用两个参数的代码使用一个参数在函数之间复制更多数据而不是我的代码。

版本1

以下程序使用建议的单参数function2()来减少main()函数与function2()之间的耦合,从而简化两者。

此代码使用命令行在Mac OS X 10.7.5上的GCC 4.7.1下编译时没有警告:

    gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
        -Wold-style-definition mem.c -o mem

valgrind下运行时,它没有内存泄露。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { MAX_INPUT = 28 };
enum { MAX_SIZE  = 3  };

struct Person
{
  char name[MAX_INPUT];
  int age;
};

static void function2(struct Person **list)
{
    struct Person *prsn = malloc(sizeof(struct Person)); 
    assert(prsn != NULL);

    char *nameInput = "This is my name";
    int ageInput = 29;    // Again!

    strcpy(prsn->name, nameInput);
    prsn->age = ageInput;
    *list = prsn;
}

int main(void)
{
    struct Person *personList[MAX_SIZE];
    int index = 0;

    function2(&personList[index++]);
    function2(&personList[index++]);
    function2(&personList[index++]);

    for (int i = 0; i < index; i++)
        free(personList[i]);

    return 0;
}

第2版

这保留了function2()的双参数版本,让它自己进行main()应该进行的计数。该程序还可以干净地编译并在valgrind下完全运行。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { MAX_INPUT = 28 };
enum { MAX_SIZE  = 3  };

struct Person
{
  char name[MAX_INPUT];
  int age;
};

static void function2(struct Person **list, int *index)
{
    struct Person *prsn = malloc(sizeof(struct Person)); 
    assert(prsn != NULL);

    char *nameInput = "This is my name";
    int ageInput = 29;    // Again!

    strcpy(prsn->name, nameInput);
    prsn->age = ageInput;
    list[(*index)++] = prsn;
}

int main(void)
{
    struct Person *personList[MAX_SIZE];
    int index = 0;

    function2(personList, &index);
    function2(personList, &index);
    function2(personList, &index);

    for (int i = 0; i < index; i++)
        free(personList[i]);

    return 0;
}

答案 1 :(得分:1)

我认为你的function2确实是

int function2(struct Person *list, int *index)

你可以用

保存你的malloc指针
list[(*index)++] = prsn;

然后在main中,您可以使用

释放列表
while(index)
    free(list[--index]);

答案 2 :(得分:0)

在面向对象的编程语言中,对象数组实际上只是指向对象的指针数组。因此,如果指针占用4个字节,而对象占用5个,则10个对象的数组实际上将是4 * 10个字节长(加上开销)。

MyClass[] my_variable = new MyClass[10]; // this will allocate 10*pointersize bytes, plus overhead, you still need to allocate more space for the objects themselves

在C中,结构数组是结构数组。如果一个结构需要8个字节,则其中10个数组需要80个字节。您不需要分配更多空间。它已经存在了。

struct data_t my_variable[10]; // this will allocate 10*sizeof(data_t) bytes

所以答案很简单:

int function2(struct Person *list, int *index) {
  char* nameInput;
  // User input code goes here ...
  // IMPORTANT: the user input code must allocate the space for nameInput
  // if it doesn't, then a copy of the buffer must be made
  // instead of the direct assignment below

  // the memory for this instance of the person was already created when the
  // array was created, so just save the values

  list[*index].name = nameInput;
  list[*index].age = ageInput;
  *index += 1; 

  return 0;
}

当您的代码执行此行时:

list[(*index)++] = *prsn; 

它实际上是复制8个字节(在32位机器上,sizeof(int)+ sizeof(char *)),因为我认为你注意到你的提交有些东西“//!为什么要使用dereferencing * prsn这里吗?“

使用malloc分配的内存的地址不会在该行中复制,但内存的内容是。这就是为什么它是内存泄漏,当函数退出时地址丢失,因为它从未放入数组中。