【问题标题】:Difference in creating a struct using malloc and without malloc使用 malloc 和不使用 malloc 创建结构的区别
【发布时间】:2020-02-02 19:09:26
【问题描述】:

有人可以向我解释一下使用和不使用 malloc 创建结构的区别吗?什么时候应该使用malloc,什么时候应该使用正则初始化?

例如:

struct person {

    char* name;

};

struct person p = {.name="apple"};

struct person* p_tr = malloc(sizeof(struct person));
p_tr->name = "apple";

两者之间的真正区别是什么?什么时候会使用一种方法而不是其他方法?

【问题讨论】:

  • sizeof(struct person)sizeof *p_tr 有效,但 sizeof(person) 是语法错误。最佳做法是使用sizeof *p_tr
  • 我想最大的实际区别是struct person p = {.name="apple"};创建了一个在封闭块结束时超出范围的变量(例如,如果这是在一个函数中,当函数返回时内存变得无效.) 使用struct person *p,变量p在封闭范围结束时也会失效,但它指向的数据仍然有效。
  • @WilliamPursell 因此,如果我将指针 *ptr 存储在某个队列中,即使在函数结束之后尝试检索它,它仍然会存在,但 person p 不是这种情况?
  • 当您需要对对象的生命周期进行更多控制时,请使用malloc,而不是第一个版本提供给您的权限
  • @WilliamPursell:自动对象的生命周期在其关联块的执行结束时结束。这与执行离开范围时不同,因为执行可能通过子例程或中断/信号离开范围。生命周期是 when 在程序执行期间 object 存在。范围是在源文本中 标识符可见的位置。

标签: c struct malloc


【解决方案1】:

如果我们假设这两个例子都发生在一个函数中,那么在:

struct person p = {.name="apple"};

C 实现自动为p 分配内存,并在函数执行结束时释放它(或者,如果语句位于嵌套在函数中的块内,则在该块执行结束时)。这在以下情况下很有用:

  • 您正在处理大小适中的对象。 (对于大型对象,使用许多千字节内存,malloc 可能更好。阈值因情况而异。)
  • 您同时处理少量对象。

在:

struct person* p_tr = malloc(sizeof(struct person));
p_tr->name = "apple";

程序显式地为对象请求内存,并且程序在处理完对象后通常应该使用free 释放该内存。这在以下情况下很有用:

  • 对象必须返回给函数的调用者。当函数执行结束时,如上所用的自动对象将不复存在(在 C 计算模型中;计算机中的实际内存不会停止存在,而只是不再为对象保留使用) ,但这个分配的对象将继续存在,直到程序释放它(或结束执行)。
  • 对象非常大。 (通常,C 实现为malloc 分配的内存比为自动对象分配的内存更多。)
  • 程序将根据具体情况创建可变数量的此类对象,例如根据在读取前大小未知的输入创建链表、树或其他结构。

注意struct person p = {.name="apple"};"apple" 初始化name 成员并将所有其他成员初始化为零。但是,使用malloc 并分配给p_tr->name 的代码不会初始化其他成员。

如果struct person p = {.name="apple"}; 出现在函数之外,则它会创建一个具有静态存储持续时间的对象。它将在程序执行期间存在。

最好使用struct person *p_tr = malloc(sizeof *p_tr);,而不是struct person* p_tr = malloc(sizeof(struct person));。对于前者,对p_tr 的更改需要在两个地方进行编辑,这使人有机会犯错误。对于后者,仅在一处更改 p_tr 的类型仍会导致请求正确的大小。

【讨论】:

    【解决方案2】:

    具有类似的数据结构;

    struct myStruct {
        int a;
        char *b;
    };
    
    struct myStruct p;  // alternative 1
    struct myStruct *q = malloc(sizeof(struct myStruct));  // alternative 2
    
    • 备选方案 1:在 stack 上分配 myStruct 宽度的内存空间并将结构的内存地址交还给您(即,&p 为您提供第一个字节地址结构)。如果它在函数中声明,它的生命在函数退出时结束(即,如果函数超出范围,则无法访问它)。

    • 备选方案 2:在上分配myStruct宽度的内存空间和在堆栈上分配(struct myStruct*)类型的内存空间指针宽度。 stack 上的指针值被分配了结构的内存地址的值(在堆上),并且这个指针地址(不是实际的structs 地址)被交还给你.在您使用 free(q) 之前,它的生命周期永远不会结束。

    在后一种情况下,假设myStruct 位于内存地址0xabcd0000 上,而q 位于内存地址0xdddd0000 上;然后,内存地址0xdddd0000上的指针值被分配为0xabcd0000,并返回给你。

    printf("%p\n", &p); // will print "0xabcd0000" (the address of struct)
    
    printf("%p\n", q);  // will print "0xabcd0000" (the address of struct)
    printf("%p\n", &q); // will print "0xdddd0000" (the address of pointer)
    

    解决你的第二部分;什么时候用哪个:

    • 如果这个struct在函数中,需要在函数退出后使用,需要malloc。您可以通过返回指针来使用结构的值,例如:return q;
    • 如果这个结构是临时的,之后你不需要它的值,你就不需要malloc内存。

    举例说明:

    struct myStruct {
        int a;
        char *b;
    };
    
    struct myStruct *foo() {
        struct myStruct p;
        p.a = 5;
        return &p; // after this point, it's out of scope; possible warning
    }
    
    struct myStruct *bar() {
        struct myStruct *q = malloc(sizeof(struct myStruct));
        q->a = 5;
        return q;
    }
    
    int main() {
        struct myStruct *pMain = foo();
        // memory is allocated in foo. p.a was assigned as '5'.
        // a memory address is returned.
        // but be careful!!!
        // memory is susceptible to be overwritten.
        // it is out of your control.
    
        struct myStruct *qMain = bar();
        // memory is allocated in bar. q->a was assigned as '5'.
        // a memory address is returned.
        // memory is *not* susceptible to be overwritten
        // until you use 'free(qMain);'
    }
    

    【讨论】:

    • 最好使用 C 标准的术语来回答 C 问题。 malloc 提供的内存是分配的heap 是一种特殊的数据结构,malloc 的实现不一定使用堆。硬件堆栈在通用 C 实现中压倒性地用于实现自动存储,但它们也不是 C 标准所要求的,并且深奥的实现(例如,对于资源受限的专用系统)可能不要使用硬件堆栈。
    • @EricPostpischil:是的,你可能是对的。我不是编译器内部的专家。
    【解决方案3】:
    struct person p = {.name="apple"};
    

    ^这是对 person 类型的变量/实例的自动分配。

    struct person* p_tr = malloc(sizeof(person));
    

    ^这是对 person 类型的变量/实例的动态分配。

    静态内存分配发生在编译时。 动态内存分配意味着它在程序执行该行指令时在运行时分配内存

    【讨论】:

    • 我该如何“利用”这种静态分配和动态分配的区别?
    • 它有什么不同?
    • 您可以通过此链接查看说明,stackoverflow.com/a/8385488/11973277。基本上,当您知道程序生命周期中需要多少内存/变量/实例时,最好预先分配内存,因为有多种原因,例如,内存将在编译时预先分配,这将节省分配时间可以在运行时使用。
    • 另一方面,动态内存分配让程序员在他/她的程序生命周期内可以更好地控制内存管理。您在运行时分配一块内存,在它使用完成后,您可以简单地释放它,或者根据您的要求和选择保留它。因此,它为您提供了更多控制权,但同时您也承担了更多责任。
    【解决方案4】:

    根据您的 cmets 判断,您对何时使用其中一个感兴趣。请注意,所有类型的分配都会保留足以容纳其中变量值的计算机内存。大小取决于变量的类型。静态分配的变量由编译器固定到内存中的某个位置。自动分配的变量由同一编译器固定到堆栈中的某个位置。动态分配的变量在程序启动之前不存在,并且在它们被“malloc”或其他函数分配之前在内存中没有任何位置。

    所有命名变量都是静态或自动分配的。动态变量是由程序分配的,但是为了能够访问它们,仍然需要一个命名变量,它是一个指针。指针是一个足够大的变量,可以保存另一个变量的地址。后者可以动态或静态或自动分配。

    问题是,如果您的程序不知道在执行期间需要使用的对象数量,该怎么办。例如,如果您从文件中读取一些数据并创建一个动态结构,如程序中的列表或树,该怎么办。您不确切知道您将拥有多少这样的结构的成员。这是动态分配变量的主要用途。您可以根据需要创建任意数量的它们并将所有内容都放在列表中。在最简单的情况下,您只需要一个指向列表开头的命名变量即可了解列表中的所有对象。

    另一个有趣的用途是当你从一个函数返回一个复杂的结构时。如果在栈上自动分配,从函数返回后将不复存在。动态分配的数据将是持久的,直到它被显式释放。因此,在这里使用动态分配会有所帮助。

    还有其他用途。

    在您的简单示例中,两种情况之间没有太大区别。第二个需要额外的计算机操作,调用'malloc'函数为你的结构分配内存。在第一种情况下,结构的内存是否分配在程序启动时定义的静态程序区域中。请注意,第二种情况下的指针也是静态分配的。它只是保留结构的内存区域的地址。

    此外,作为一般规则,动态分配的数据最终应由“释放”函数释放。您无法释放静态数据。

    【讨论】:

      猜你喜欢
      • 2016-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-30
      • 1970-01-01
      • 2014-04-07
      • 1970-01-01
      • 2021-11-22
      相关资源
      最近更新 更多