【问题标题】:Not sure whether or not to malloc memory for a struct不确定是否为结构分配内存
【发布时间】:2015-12-07 21:34:38
【问题描述】:

假设我有以下 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;
}

其中get_person_info() 只是填写了传递指针的person_t 结构。是否有必要在main() 内为new_person 分配malloc() 内存?也就是应该行

person_t new_person;

改为

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

然后将get_person_info() 更改为接受person_t ** 而不是person_t *

对不起,如果这个问题令人困惑——我不确定这是否是需要保留内存的情况,因为指向该内存的指针被传递到 get_person_info() 以避免导致分段错误.

【问题讨论】:

  • 两者都是正确的。第一个在栈上分配内存,第二个在堆上分配内存。
  • 嗯...但是如果 new_person 不会在另一个函数中使用并且仅用于调用 get_person_info()main() 内,那么将其存储在堆栈中而不打扰 @ 是否安全987654335@ing 内存呢?
  • 两者都有效。要确定哪个更好,需要看看new_person后面是如何在代码中使用的。
  • 当块结束时,堆栈上的对象被销毁。如果您需要完全控制对象的生命周期,则应在堆上为其分配内存。
  • 请注意,由于问题是用 C 而不是 C++ 标记的,因此转换 malloc 的返回值是多余的。

标签: c pointers segmentation-fault malloc dynamic-memory-allocation


【解决方案1】:

两者都是正确的,这取决于您想在哪里使用person_info。 在栈上分配:

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

在堆栈上创建一个person_t 对象并用数据填充new_person 对象,因为循环只这样做,对象在下一次循环迭代时超出范围并且数据丢失。

使用 malloc :

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

在堆上创建一个person_t 对象并用数据填充它,因为它在堆上分配的new_person 对象将超过循环范围,这意味着你正在泄漏内存,因为你没有指向的指针上一个循环周期的person_t对象的数据。

【讨论】:

  • 请注意,由于问题是用 C 而不是 C++ 标记的,因此转换 malloc 的返回值是多余的。
【解决方案2】:

两种方法都是正确的!!

person_t *new_person = (person_t *) malloc(sizeof(person_t)); 然后将 get_person_info() 更改为接受 person_t ** 而不是 person_t *?

你不需要改变函数的参数-void get_person_infperson_t *person);。只需像这样在main中传递指向它的指针-

get_person_info(new_person);

但是在没有分配内存的先前方式中,您将无法在定义它的块之外使用它,而如果您的程序依赖于它的生命,您可以在堆上为其分配内存。

在您发布的代码中,new_person 仅在 loop 内部使用,因此如果您不打算使用外部循环,您可能不需要 动态分配

但是如果你想在loop 之外使用它,你也应该使用动态分配。但是不要忘记free它。

【讨论】:

    【解决方案3】:

    不确定是否为结构体malloc 内存?

    简短的回答是:在你的情况下不需要这样做。如果你想在forloop 之外使用你的对象,你可以通过动态分配的内存来实现,即:

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

    然后调用它:

     get_person_info(new_person);
    

    在你的例子中,对象是在循环中使用的,因此没有必要这样做。

    注意:

    当您使用动态分配的内存时,您应该始终释放它,最后避免内存泄漏。

    编辑:

    正如@Johann Gerell 所指出的,在消除了 malloc 返回类型的强制转换的冗余之后,在 C 中,分配将如下所示:

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

    malloc 返回一个 void 指针 (void *),表示它是一个指向未知数据类型区域的指针。由于强类型系统,在 C++ 中需要使用强制转换,而在 C 中则不然。

    【讨论】:

    • 请注意,由于问题是用 C 而不是 C++ 标记的,因此转换 malloc 的返回值是多余的。
    • @Johann Gerell 谢谢!我应该编辑它还是添加一行:person_t *new_person = malloc(sizeof(person_t));
    • 我会完全删除强制转换 - 这是一个 C 问题,而不是 C++ ;-)
    【解决方案4】:

    您的困惑源于没有很好地理解对象存储持续时间和指针。让我们分别查看每一个以了解清楚。

    存储时间

    一个对象可以有自动或动态的存储期限。

    自动

    顾名思义,自动将由编译器为您管理。您只需定义一个变量,使用它,当它超出范围时,对象会自动为您销毁。一个简单的例子:

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

    当控件进入if的作用域时,将自动分配足以容纳int的内存并赋值0。一旦您对i 的使用结束,当控件退出范围时,名称i 将超出范围,因此程序将无法再访问,并且自动为您分配的存储区域也将被回收。

    动态

    假设您想要动态分配对象,即您想要管理存储,从而管理对象的生命周期,而没有范围或编译器妨碍您,那么您将继续从平台请求存储空间使用malloc

    malloc(sizeof(int));
    

    请注意,我们没有将 malloc 的返回值分配给您过去看到的任何指针。我们稍后会谈到指针,现在让我们完成动态对象。在这里,足以容纳int 的空间由malloc 交给您。完成后由您决定free。因此,这个未命名的int 对象的生命周期掌握在您手中,并且超出了创建它的代码的范围。只有当您明确调用free 时,它才会结束。如果没有匹配的 free 呼叫被调用,您将拥有臭名昭著的 memory leak

    指针

    指针就像它的名字所说的那样——一个可以引用另一个对象的对象。指针永远不是它所指向的 (pointee)。指针是一个对象,它的指针是另一个独立的、独立的对象。您可以将指针指向另一个命名对象、未命名对象或什么都没有 (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
    

    因此,大多数人将指针与动态分配的指针混淆的原因在于:指针在这里没有名称,因此它们总是通过指针来引用;有些人把一个误认为另一个。

    功能接口

    这里采用指针的函数是合适的,因为从调用者的角度来看,它是一个灵活的函数:它可以采用自动和动态对象。我可以创建一个自动变量并传入,也可以传递一个动态变量:

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

    malloc()内存需要new_person within main()吗?

    没有。您可以定义一个自动变量并将其传递给函数。事实上,建议您尽量减少对动态对象的使用,并更喜欢自动对象,因为

    • 它可以最大限度地减少代码中内存泄漏的可能性。即使是经验丰富的程序员也会错过将匹配的free 调用为malloc,从而导致内存泄漏。
    • 动态对象分配/释放比自动变量分配/释放慢得多。
    • 大量动态分配解除分配会导致内存碎片。

    但是,自动变量通常分配在堆栈中,因此您可以在堆栈上创建的数量和大小的上限相对低于您可以动态分配的数量(通常从堆中)。

    get_person_info() 更改为接受person_t ** 而不是person_t *

    不,如果你这样做了,传递自动变量的选项仍然是可能的,但很麻烦:

    void foo(int **o);
    
    int i = 0;
    int *p = &i;  // p is redundant
    foo(&p);
    
    int *p = malloc(sizeof(int));
    foo(&p);
    

    相对简单

    void bar(int *o);
    
    int i = 0;
    bar(&i);
    
    int *p = malloc(sizeof(int));
    bar(p);
    

    【讨论】:

      猜你喜欢
      • 2016-05-06
      • 1970-01-01
      • 2016-01-27
      • 2011-06-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-20
      • 2019-09-23
      相关资源
      最近更新 更多