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

use*_*888 0 c malloc valgrind pointers

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

我通过Valgrind运行程序,我得到"仍然可以访问"的字节(这应该是正确的?不被认为是泄漏),但是任何保存的数据我都会丢失块.

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

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

我声明了以下结构:

struct Person {
  char name[MAX_INPUT];
  int age;
};
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

这是我的主要功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

先感谢您.

编辑:修复了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)
Run Code Online (Sandbox Code Playgroud)

Jon*_*ler 6

代码分析

让我们一次解剖一下:

int function2(struct Person *list) {
Run Code Online (Sandbox Code Playgroud)

正如Iserni在他的回答中指出的那样,这个定义与你对函数的调用不一致.我一般同意他对现有代码的更正(但我建议不久修改它):

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.
Run Code Online (Sandbox Code Playgroud)

这取决于你是用C还是C++; 在C++中,你必须转换返回值malloc()(如果你使用的话malloc();你通常不应该在C++中).在C中,演员阵容是可选的.有一种想法,省略演员表明插入演员可以隐藏的错误.我不同意; 我认为malloc()应该已经<stdlib.h>声明了并且如果范围中没有声明,编译器应该警告,并且如果范围中有声明,则演员不能掩盖罪行.另一个可能的问题是你指定一个非指针的指针; 这也是编译器应该抱怨的错误.

  assert(prsn != NULL);
Run Code Online (Sandbox Code Playgroud)

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

  // 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?
Run Code Online (Sandbox Code Playgroud)

因为:

  • list是一个struct Person *.
  • 因此list[i]是一个struct Person(尽管你拼写i(*index)++).
  • 因此,您必须为其分配一个struct Person.
  • prsn是一个struct Person *.
  • 因此*prsn也是一个struct Person.
  • 因此,分配是"正确的".
  • 它还会给你泄漏.
  • 你已经覆盖的内容list[i]与内容*prsn.
  • 你没有将指针保存到prsn任何地方.
  • 因此,当您从函数返回时泄漏内存.

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

int function2(struct Person **item)
...
*item = prsn;
Run Code Online (Sandbox Code Playgroud)

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

  // why not directly prsn? Or is that saving the memory address and not very useful.
}
Run Code Online (Sandbox Code Playgroud)

您的函数被声明为返回int但是您显示没有return.如果您不打算返回值,请将该函数声明为void,特别是如果您要忽略返回值,就像您的代码main()一样.

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

这是我的主要功能:

int main(int argc, char *argv[]) { 
  struct Person personList[MAX_SIZE];
  int index;
Run Code Online (Sandbox Code Playgroud)

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

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

  ...other code...

  function2(personList, &index);
Run Code Online (Sandbox Code Playgroud)

修改后的功能:

  function2(&personList[index++]);
Run Code Online (Sandbox Code Playgroud)

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

  // Before closing, I want to free any mallocs I have done here. free()
Run Code Online (Sandbox Code Playgroud)

所以你写道:

  for (int i = 0; i < index; i++)
      free(personList[i]);
Run Code Online (Sandbox Code Playgroud)

这将释放分配的所有内存.

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

确保在编译时启用了足够的警告.如果您正在使用GCC,那么gcc -Wall应该是您运行的最低编译警告级别(当您这样做时,代码中应该没有警告).我运行的是更严格的警告:gcc -std=c99 -Wall -Wextra -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes -Wshadow通常.您需要包含-O3(或某种程度的优化)才能获得一些警告.关于原型的东西反映了我使用的旧代码库的偏执狂,它仍然有K&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
Run Code Online (Sandbox Code Playgroud)

在运行时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;
}
Run Code Online (Sandbox Code Playgroud)

版本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;
}
Run Code Online (Sandbox Code Playgroud)