繁体   English   中英

使用 malloc 和不使用 malloc 创建结构体的区别

[英]Difference in creating a struct using malloc and without malloc

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

例如:

struct person {

    char* name;

};

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

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

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

具有像这样的数据结构;

struct myStruct {
    int a;
    char *b;
};

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

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

在后一种情况下,例如, myStruct位于内存地址0xabcd0000q位于内存地址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);'
}

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

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

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

  • 您正在处理中等大小的对象。 (对于大对象,使用许多 kibibytes 的内存, malloc可能更好。阈值因情况而异。)
  • 您一次处理少量对象。

在:

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

该方案明确要求内存对象,程序一般应释放内存, free当这个对象。 这在以下情况下很有用:

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

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

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

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

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

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

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

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

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

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

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

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

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

还有其他用途。

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

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

暂无
暂无

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

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