简体   繁体   English

不确定是否为结构体分配内存

[英]Not sure whether or not to malloc memory for a struct

Suppose I have the following C code: 假设我有以下C代码:

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

#define NUM_PEOPLE 24

typedef struct {
    char **name;
    int age;
} person_t;

void get_person_info(person_t *person);

int main(int argc, char **argv) {
    for (int i = 0; i < NUM_PEOPLE; i++) {
        person_t new_person;
        get_person_info(&new_person);
    }

    return 0;
}

where get_person_info() just fills out the person_t struct to which a pointer is passed in. Is it necessary to malloc() memory for new_person within main() ? 其中, get_person_info()仅填充将指针传递到的person_t结构。是否有必要在main()new_person malloc()内存? That is, should the line 也就是说,该行

person_t new_person;

instead be 而是

person_t *new_person = (person_t *) malloc(sizeof(person_t));

and then change get_person_info() to accept a person_t ** instead of a person_t * ? 然后将get_person_info()更改为接受person_t **而不是person_t *

Sorry if this question is confusing -- I'm not sure whether or not this is a case where it is necessary to reserve memory, given that a pointer to that memory is passed into get_person_info() to avoid causing a segmentation fault. 很抱歉,如果这个问题令人困惑-我不确定是否需要保留内存,因为指向该内存的指针已传递给get_person_info()以避免引起分段错误。

Both are correct, it depends on where you want to use the person_info . 两者都是正确的,这取决于您要在何处使用person_info Allocating on the stack : 在堆栈上分配:

 for (int i = 0; i < NUM_PEOPLE; i++) {
        person_t new_person;
        get_person_info(&new_person);
    }

Creates a person_t object on the stack and fills the new_person object with data, because the loop only does that, the object goes out of scope on the next loop iteration and the data is lost. 在堆栈上创建一个person_t对象,并用数据填充new_person对象,因为循环仅这样做,该对象在下一次循环迭代时超出范围,并且数据丢失。

Using malloc : 使用malloc:

for (int i = 0; i < NUM_PEOPLE; i++) {
        person_t *new_person = malloc(sizeof(person_t));
        get_person_info(new_person);
    }

Creates a person_t object on the heap and fills it with data, because its allocated on the heap the new_person object will outlive the loop scope which currently means that you're leaking memory because you have no pointer pointing at the data of the person_t object of the previous loop cycle. 在堆上创建一个person_t对象,并用数据填充它,因为person_t对象在堆上分配的new_person超过了循环范围,这当前意味着您正在泄漏内存,因为您没有指针指向该对象的person_t对象的数据上一个循环周期。

Both ways are correct !! 两种方法都是正确的!

person_t *new_person = (person_t *) malloc(sizeof(person_t)); and then change get_person_info() to accept a person_t ** instead of a person_t *? 然后更改get_person_info()以接受person_t **而不是person_t *?

you don't need to change parameter of function - void get_person_infperson_t *person); 您不需要更改函数的参数void get_person_infperson_t *person); .Just pass pointer to it in main like this - 只需像这样在main传递指向它的指针-

get_person_info(new_person);

But in previous way without allocating memory , you won't be able to use it outside the block it is defined in whereas if your program depend on its life you can allocate memory to it on heap. 但是以以前的方式,如果不分配内存,您将无法在定义它的块之外使用它,而如果您的程序依赖于它的生命,则可以在堆上分配内存。

In your code you posted new_person is used inside loop only so if you don't intend to use to outside loop you probably won't need dynamic allocation . 在您的代码中,您发布的new_person仅在内部loop使用,因此,如果您不打算在外部循环中使用,则可能不需要动态分配

But if you want to use it outside loop also you should use dynamic allocation. 但是,如果要在loop外使用它,则还应该使用动态分配。 But don't forget to free it. 但是不要忘记free它。

Not sure whether or not to malloc memory for a struct? 不知道是否要malloc内存,一个结构?

The short answer is: no need to do it in your case . 简短的答案是: 无需根据您的情况进行操作 If you want to use your object outside the for loop you could do it by dynamically allocated memory, namely: 如果要在for循环外使用对象for可以通过动态分配的内存来实现,即:

person_t *new_person = malloc(sizeof(person_t));

and then call it with: 然后调用:

 get_person_info(new_person);

In you example, the object is used within the loop, thus there is no need to do it. 在您的示例中,该对象在循环内使用,因此无需这样做。

Note: 注意:

when you use dynamically allocated memory you should always free it, at the end to avoid memory leaks. 使用动态分配的内存时,应始终释放它,最后避免内存泄漏。

Edit: 编辑:

As pointed out by @Johann Gerell, after removing the redundancy of the casting of the return type of malloc, in C, the allocation would look like: 正如@Johann Gerell指出的那样,在C语言中删除malloc返回类型的强制类型转换的冗余之后,分配看起来像:

person_t *new_person = malloc(sizeof(person_t));

malloc returns a void pointer ( void * ), which indicates that it is a pointer to a region of unknown data type. malloc返回一个void指针( void * ),这表明它是指向未知数据类型区域的指针。 The use of casting is required in C++ due to the strong type system, whereas this is not the case in C. 由于强类型系统,在C ++中需要使用强制转换,而在C中则不是这种情况。

Your confusion stems from not understanding object storage duration and pointers well. 您的困惑源于对对象存储持续时间和指针的理解不深。 Let's see each one separately to get some clarity. 让我们分别查看每个对象,以使内容更清晰。

Storage Duration 储存期限

An object can have automatic or dynamic storage duration. 一个对象可以具有自动或动态存储持续时间。

Automatic 自动

Automatic, as the name says, would be managed by the compiler for you. 顾名思义,自动将由编译器为您管理。 You just define a variable, use it and when it goes out of scope the object is destroyed automatically for you. 您只需定义一个变量,使用它,当它超出范围时,该对象将自动为您销毁。 A simple example: 一个简单的例子:

if (flag) {
    int i = 0;
    /* some calc. involving i */
}
// i is dead here; it cannot be accessed and its storage is reclaimed

When the control enters the if 's scope, memory large enough to hold an int will be allocated automatically and assigned the value 0 . 当控件进入if的范围时,将自动分配足够容纳int内存,并为其分配值0 Once your use of i is over, when the control exits the scope, the name i goes out of scope and thus will no longer be accessible by the program and also its storage area allocated automatically for you would be reclaimed. 一旦您对i的使用结束,则当控件退出范围时,名称i将超出范围,因此程序将不再可访问它,并且将自动回收为其自动分配的存储区。

Dynamic 动态

Lets say you want to have objects dynamically allocated ie you want to manage the storage and thereby the lifetime of the object without the scope or the compiler coming in your way, then you'd go on by requesting storage space from the platform using malloc 假设您要动态分配对象,即要管理存储,从而在不影响范围或编译器的情况下管理对象的生存期,然后继续使用malloc向平台请求存储空间

malloc(sizeof(int));

Notice that we're not assigning the return value of malloc to any pointer as you're used to seeing. 请注意,正如您经常看到的那样,我们没有将malloc的返回值分配给任何指针。 We'll get to pointers in a bit, lets finish dynamic objects now. 我们将稍等一下指针,让我们现在完成动态对象。 Here, space large enough to hold an int is handed over to you by malloc . 在这里, malloc将足够大的空间容纳一个int It's up to you to free it when you're done with it. 完成操作后,您可以自行free它。 Thus the lifetime of this unnamed int object is in your hands and would live beyond the scope of the code that created it. 因此,这个未命名的int对象的生命周期掌握在您的手中,并且超出了创建它的代码范围。 It would end only when you explicitly call free . 仅当您显式调用free时,它才会结束。 Without a matching free call getting called, you'd have the infamous memory leak . 如果没有匹配的free电话被调用,您将臭名昭著的内存泄漏

Pointers 指针

A pointer is just what its name says - an object that can refer to another object. 指针就是它的名字所说的-一个可以引用另一个对象的对象。 A pointer is never what it is pointing at ( pointee ). 指针永远不会指向( pointee )。 A pointer is an object and its pointee is another separate, independent object. 指针是一个对象,其指针是另一个单独的独立对象。 You may make a pointer point to another named object, unnamed object, or nothing ( NULL ). 您可以使一个指针指向另一个命名对象,未命名对象或不指向任何对象( NULL )。

int i = 0;
int *ptr1 = &i;                  // ptr1 points to the automatic int object i
int *ptr2 = malloc(sizeof(int)); // ptr2 points to some unnamed int object
int *ptr3 = NULL;                // ptr3 points to nothing

Thus the reason most people confuse pointers for dynamically allocated pointees comes from this: the pointee, here, doesn't have a name and hence they're referred to always via their pointers; 因此,大多数人将指针混淆为动态分配的指针的原因来自此:指针在这里没有名称,因此始终通过指针来引用它们。 some people mistake one for the other. 有些人将一个人误认为另一个人。

Function Interface 功能界面

The function taking a pointer is appropriate here, since from the caller's viewpoint it's a flexible function: it can take both automatic and dynamic objects. 带有指针的函数在这里是合适的,因为从调用者的角度来看,它是一个灵活的函数:它可以同时包含自动对象和动态对象。 I can create an automatic variable and pass it in, or I can pass a dynamic variable too: 我可以创建一个自动变量并将其传递,也可以传递一个动态变量:

void get_person_info(person_t *person);

person_t o { };
get_person_info(&a);

person_t *p = malloc(sizeof(person_t));
get_person_info(p);
free(p);

Is it necessary to malloc() memory for new_person within main() ? 是否有必要new_person within main()new_person within main() malloc()内存?

No. You can define an automatic variable and pass it to the function. 不可以。您可以定义一个自动变量并将其传递给函数。 In fact it's recommended that you try to minimize your usage of dynamic objects and prefer automatic objects since 实际上,建议您尽量减少对动态对象的使用,而首选自动对象,因为

  • It minimizes the chances of memory leaks in your code. 它最大程度地减少了代码中内存泄漏的机会。 Even seasoned programmers miss calling the matching free to a malloc thereby introducing a memory leak. 即使是经验丰富的程序员也会错过对malloc free调用匹配,从而导致内存泄漏。
  • Dynamic object allocation/deallocation is far slower than automatic variable allocation/deallocation. 动态对象分配/取消分配比自动变量分配/取消分配要慢得多。
  • A lot of dynamic allocation deallocation causes memory fragmentation. 许多动态分配重新分配会导致内存碎片。

However, automatic variables are generally allocated in the stack and thus the upper limit on the number and size on how much you can create on the stack is relatively lower than what you can allocate dynamically (generally from the heap). 但是,自动变量通常在堆栈中分配,因此,在堆栈上可以创建的数量和数量的上限相对于可以动态分配(通常是从堆中分配)的数量相对较低。

change get_person_info() to accept a person_t ** instead of a person_t * ? 更改get_person_info()以接受person_t **而不是person_t *吗?

No, if you did so, the option of passing automatic variables would still be possible but cumbersome: 不,如果您这样做的话,传递自动变量的选项仍然可能,但是很麻烦:

void foo(int **o);

int i = 0;
int *p = &i;  // p is redundant
foo(&p);

int *p = malloc(sizeof(int));
foo(&p);

As opposed the simpler 相对更简单

void bar(int *o);

int i = 0;
bar(&i);

int *p = malloc(sizeof(int));
bar(p);

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

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