简体   繁体   English

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

[英]malloc() struct in a function, free() before ending program

I am learning the basics of C, and now working with malloc(). 我正在学习C的基础知识,现在正在使用malloc()。 Say I have a function that asks the user for input, populates a structure with said data, and that structure in saved in an array (all passed by reference from a main function). 说我有一个函数,要求用户输入,用所述数据填充一个结构,然后将该结构保存在数组中(所有这些都是通过引用从主函数传递的)。

I ran the program through Valgrind, I get the "still reachable" bytes (which should be ok right? not considered a leak), but any data saved I get lost blocks. 我通过Valgrind运行了程序,我得到了“仍然可以到达”的字节(应该可以吗?不是泄漏),但是保存的所有数据都丢失了块。

How would I go freeing that memory after the program has finished running? 程序完成运行后,我将如何释放该内存? Also I have some (2) questions within the code just to clarify some things and would appreciate if someone could explain them to me. 我在代码中也有一些(2)问题,只是为了澄清一些事情,如果有人可以向我解释它们,我将不胜感激。

Here is some code similar to what I am trying to do: 这是一些与我正在尝试执行的代码类似的代码:

I have the following struct declared: 我声明了以下结构:

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

I am writing a function that goes like this: 我正在写一个像这样的函数:

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;
}

And this is my main function: 这是我的主要功能:

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 report: 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

Thank you in advance. 先感谢您。

Edit: Fixed function2 parameters, return and other things. 编辑:修复了function2参数,返回等问题。 I apologize, was writing it quickly to illustrate my main question about freeing memory. 我很抱歉,我正在迅速编写它来说明我有关释放内存的主要问题。 Thanks for the corrections tips, but the real code is actually compiling correctly and everything. 感谢您提供的更正提示,但实际的代码实际上可以正确编译所有内容。

Edit2: After adding a simple loop at the end of main like suggested to use free(), I get the following errors. 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)

Code Analysis 代码分析

Let's dissect this a bit at a time: 让我们一次剖析一下:

int function2(struct Person *list) {

As Iserni noted in his answer , this definition is inconsistent with your call to the function. 正如Iserni回答中指出的那样,此定义与您对该函数的调用不一致。 I agree in general with his correction for the existing code (but I'm going to recommend modifying it shortly): 我总体上同意他对现有代码的更正(但我建议您尽快对其进行修改):

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.

It depends on whether you're in C or C++; 这取决于您使用C还是C ++。 in C++, you must cast the return value from malloc() (if you use malloc() at all; you usually shouldn't in C++). 在C ++中,必须malloc()的返回值(如果完全使用malloc() ,通常不应该在C ++中使用)。 In C, the cast is optional. 在C中,强制类型转换是可选的。 There is a school of thought that omitting the cast reveals errors that inserting the cast can hide. 有一种流派认为,省略演员表会发现插入演员表可能隐藏的错误。 I don't subscribe to that; 我不同意; I believe that malloc() should have been declared via <stdlib.h> and the compiler should be warning if there is no declaration in scope, and if there's a declaration in scope, the cast can't cover up sins. 我相信malloc()应该已经通过<stdlib.h>进行了声明,如果范围内没有声明,并且如果范围内有声明,则编译器应发出警告,强制类型转换无法掩盖罪恶。 The other possible problem is that you assign a pointer to a non-pointer; 另一个可能的问题是您将指针分配给非指针。 that would be a mistake that the compiler should be complaining about too. 编译器也应该抱怨这一错误。

  assert(prsn != NULL);

This is not normally considered a sensible long-term method of handling memory allocation errors. 通常,这不是处理内存分配错误的明智的长期方法。

  // 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?

Because: 因为:

  • list is a struct Person * . list是一个struct Person *
  • Therefore list[i] is a struct Person (albeit you spelled i as (*index)++ ). 因此, list[i]是一个struct Person (尽管您将i拼写为(*index)++ )。
  • Therefore you must assign a struct Person to it. 因此,您必须为其分配一个struct Person
  • prsn is a struct Person * . prsn是一个struct Person *
  • Therefore *prsn is also a struct Person . 因此, *prsn也是struct Person
  • Therefore the assignment is 'correct'. 因此,分配是“正确的”。
  • It also gives you the leak. 它还会给您泄漏。
  • You've overwritten the content of list[i] with the content of *prsn . 您已经用*prsn的内容覆盖了list[i]的内容。
  • You've not save the pointer to prsn anywhere. 您尚未在任何地方保存指向prsn的指针。
  • So you leak memory as you return from the function. 因此,您从函数返回时会泄漏内存。

The surgery required to fix this is non-negligible: 解决此问题所需的手术不可忽略:

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

and you have to revise the call; 而且您必须修改通话; I'll come back to that when dissecting main() . 剖析main()时,我会回到这一点。

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

Your function is declared to return an int but you show no return . 您的函数声明为返回int但不显示return If you aren't going to return a value, declare the function as void , especially if you're going to ignore the return value as your code in main() does. 如果您不打算返回值,则将该函数声明为void ,尤其是当您要像main()中的代码那样忽略返回值时。

Your last comment is mostly covered by the discussion above; 您的最后评论大部分已包含在上面的讨论中; saving the memory address is crucial to stopping the leak so it is very useful. 保存内存地址对于阻止泄漏至关重要,因此非常有用。

And this is my main function: 这是我的主要功能:

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

Using an uninitialized variable is bad news. 使用未初始化的变量是个坏消息。 It is at best only accidentally zeroed. 充其量只是意外地将其清零。 You can't afford to have random values in use as array indexes; 您不能承受将随机值用作数组索引的情况; initialize it explicitly. 明确初始化它。 Also, we're going to need an array of pointers, not an array of structures: 另外,我们将需要一个指针数组,而不是一个结构数组:

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

  ...other code...

  function2(personList, &index);

With the revised function: 修改后的功能:

  function2(&personList[index++]);

This is preferable; 这是可取的。 it means that function2() doesn't need to know about the array; 这意味着function2()不需要了解数组; you just pass it the address of the pointer to which the allocated memory pointer should be assigned. 您只需将分配的内存指针应分配给的指针地址传递给它即可。 This reduces the coupling between your main() function and function2() , which makes the code simpler all around. 这样可以减少main()函数和function2()之间的耦合,从而使代码更简单。

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

So you write: 所以你写:

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

This releases all the memory allocated. 这将释放所有分配的内存。

  return 0;
}

I like to see an explicit return at the end of main() , even though C99 says it isn't 100% necessary. 我喜欢在main()的末尾看到显式的返回,即使C99表示不是100%必需的。

Make sure you are compiling with enough warnings enabled. 确保在编译时启用了足够的警告。 If you're using GCC, then gcc -Wall should be the minimum level of compilation warning you run with (and you should have no warnings in your code when you do so). 如果您使用的是GCC,则gcc -Wall应该是运行时的最低编译警告级别(这样做时,代码中不应包含警告)。 I run with more stringent warnings: gcc -std=c99 -Wall -Wextra -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes -Wshadow usually. 我以更严格的警告运行: gcc -std=c99 -Wall -Wextra -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes -Wshadow通常。 You need to include -O3 (or some level of optimization) to get some warnings. 您需要包括-O3 (或某种程度的优化)以获得一些警告。 The stuff about prototypes reflects paranoia about an old code-base I work with which still has K&R function definitions around. 关于原型的内容反映了我使用的旧代码库的偏执狂,我仍然使用它来定义K&R函数定义。


Answering comments 回答评论

First question while I go over your details and try things out: is there a memory impact between an array of structs and an array of pointers? 当我仔细研究您的详细信息并进行尝试时,第一个问题是:结构数组与指针数组之间是否会对内存产生影响?

Yes, but it may not be what you're thinking of. 是的,但这可能不是您的想法。 If you use an array of structures, there's no need to allocate memory dynamic for the structures since the compiler's already allocated them for you. 如果使用结构数组,则无需为结构分配动态内存,因为编译器已经为您分配了它们。 Since the purpose of the exercise is to use pointers and malloc() , it is better, therefore, to use pointers. 由于练习的目的是使用指针和malloc() ,因此最好使用指针。 In terms of space, there will be slightly more total memory used with the array of pointers (but there will be less memory leaked). 就空间而言,与指针数组一起使用的总内存将稍微多一些(但将会减少内存泄漏)。

I am trying to change my code to use an array of pointers. 我正在尝试将代码更改为使用指针数组。 But using function2(personList, &index); 但是使用function2(personList, &index); to call function2 now gives me the following warning: incompatible pointer types passing 'struct Person *[512]' to parameter of type 'struct Person *' . 调用function2现在会给我以下警告: incompatible pointer types passing 'struct Person *[512]' to parameter of type 'struct Person *' Is it OK if I write extra code in my main question to go into details? 如果我在主要问题中写了一些额外的代码以进入细节,可以吗? As a note, I'm trying to reference variables as much as possible, so as to not temporarily have the program copy data from function to function. 需要注意的是,我试图尽可能多地引用变量,以免程序暂时在函数之间复制数据。

The compiler is correct if you've not made all the changes. 如果尚未进行所有更改,则编译器是正确的。 Your code using two arguments copies more data between functions than my code using one argument. 与使用一个参数的代码相比,使用两个参数的代码在函数之间复制的数据更多。

Version 1 版本1

The following program uses the proposed single-argument function2() to reduce the coupling between the main() function and function2() , simplifying both. 下面的程序使用建议的单参数function2()来减少main()函数和function2()之间的耦合,从而简化两者。

This code compiles with no warnings under GCC 4.7.1 on Mac OS X 10.7.5 using the command line: 在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

When run under valgrind , it comes up with no memory leaked. 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;
}

Version 2 版本2

This keeps the two-argument version of function2() and has it do the counting that main() should be doing on its own. 这保留了function2()的两个参数的版本,并让main()自己进行计数。 This program also compiles cleanly and runs cleanly under valgrind . 该程序还可以在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;
}

I assume that your function2 is really 我认为你的function2确实是

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

You would save your malloc'ed pointer there with 您将在其中保存malloc的指针

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

Then in main you would free the list with 然后在main你会释放列表

while(index)
    free(list[--index]);

In object oriented programming languages, an array of objects is really just an array of pointers to the objects. 在面向对象的编程语言中,对象数组实际上只是指向对象的指针数组。 So if a pointer takes 4 bytes, and an object takes 5, an array of 10 objects will actually be 4*10 bytes long (plus overhead). 因此,如果一个指针占用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

In C, an array of structures is an array of structures. 在C语言中,结构数组是结构数组。 If a structure takes 8 bytes, an array of 10 of them takes 80 bytes. 如果一个结构占用8个字节,则其中的10个数组占用80个字节。 You don't need to allocate even more space. 您不需要分配更多的空间。 It's already there. 它已经在那里。

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

So the easy answer is: 因此,简单的答案是:

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;
}

When your code executes this line: 当您的代码执行此行时:

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

It is quite literally copying 8 bytes (on a 32 bit machine, sizeof(int) + sizeof(char*)), as I think you noticed something was up with your commment "// !Why use the dereferencing *prsn here?" 它实际上是复制8个字节(在32位计算机上,为sizeof(int)+ sizeof(char *)),因为我认为您注意到“ //!”,为什么在这里使用解引用* prsn?

The address to the memory allocated with malloc isn't being copied in that line, but the contents of the memory are. 用malloc分配的内存地址不会在该行中复制,但是内存的内容会被复制。 That's why it is a memory leak, the address is lost when the function exits because it was never put into the array. 这就是内存泄漏的原因,函数退出时地址就丢失了,因为它从未被放入数组中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM