我何时需要使用malloc来分配内存?

10

1)
我应该在哪些数据类型上使用malloc来分配内存?

  • 例如结构体、指针等非基本数据类型,但基本数据类型如int则不需要。
  • 所有类型都需要使用吗?

2)
为什么这段代码可以运行?为什么它不崩溃?我认为我需要先为结构体分配内存。

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

typedef unsigned int uint32;
typedef struct 
{
  int a;
  uint32* b;
}
foo;

int main(int argc, char* argv[])
{
 foo foo2;
 foo2.a = 3;
 foo2.b = (uint32*)malloc(sizeof(uint32));
 *foo2.b = 123;
}

使用...不是更好吗?

foo* foo2 = malloc(sizeof(foo));

3) foo.b是如何被设置的?它是否引用了随机内存或者为NULL?

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

typedef unsigned int uint32;
typedef struct 
{
  int a;
  uint32* b;
}
foo;

int main(int argc, char* argv[])
{
 foo foo2;
 foo2.a = 3;

}
7个回答

18
在 C 中,所有类型都可以动态、自动(在堆栈上)或静态地分配。问题不在于类型,而是在于您想要的生命周期 - 当您希望对象存在于创建它的函数的范围之外时,或者当您事先不知道需要多大的东西时,可以使用 malloc。

1
C99允许使用堆栈分配变长结构(好吧,数组,但这已足以完成所有操作)。但是我不建议在任何重要情况下使用它;实际上,堆栈往往比堆小得多。 (我还经常使用数据结构生命周期与堆栈帧生命周期不匹配的代码。) - Donal Fellows
+1和+1。可以通过将malloc()提升到调用函数而不是强制调用malloc()数百万次来减轻exo函数范围要求,这通常也是一个很大的性能改进。即使大小未知,也可以这样做,并且通常应该这样做,其中函数在内存不足时调用realloc()。我之所以这样说,是因为调用者需要指针来释放内存,一旦循环完成调用您的函数。始终在相同的范围内使用malloc()/ calloc()和free()。始终! - user1899861

9

编辑以回答您的编号问题。

  1. There are no data types you must allocate with malloc. Only if you want a pointer type to point to valid memory must you use the unary & (address-of) operator or malloc() or some related function.

  2. There is nothing wrong with your code - the line:

    foo foo2;
    

    Allocates a structure on the stack - then everything works as normal. Structures are no different in this sense than any other variable. It's not better or worse to use automatic variables (stack allocation) or globals or malloc(), they're all different, with different semantics and different reasons to choose them.

  3. In your example in #3, foo2.b's value is undefined. Any automatic variable has an undefined and indeterminate value until you explicitly initialize it.


3
你必须使用malloc为需要手动管理的内存分配空间,而不是自动管理。无论存储在其中的是intdoublestruct或其他任何东西,都没有关系;malloc与手动内存管理有关。
当你创建一个没有使用malloc的变量时,它会存储在堆栈上,并且当它超出作用域时,其内存会被自动回收。当变量不再可访问时,例如在声明变量的块或函数结束时,该变量就会超出作用域。
当你使用malloc分配内存时,它会存储在上,并且malloc返回指向该内存的指针。除非调用free释放它,否则该内存不会被回收,无论是否仍有指向它的指针(当没有指向堆分配的内存的指针时,这就是内存泄漏)。这意味着你可以在分配内存的块或函数结束后继续使用已分配的内存,但另一方面,你现在有责任在完成使用后手动释放它。
在你的示例中,foo2存储在堆栈上,并且当main结束时将自动释放。但是,由foo2.b指向的内存不会被自动释放,因为它位于堆上。这在你的示例中不是问题,因为程序结束时所有内存都会返回给操作系统,但如果它在除main之外的函数中,则会导致内存泄漏。

2

foo foo2;会自动在栈上分配结构体的内存,在包含函数结束时(在本例中为main函数)会自动释放。

只有当需要结构体在包含作用域结束后仍然存在时,才需要使用malloc在堆上分配内存。如果对象过大无法在栈上容纳,也可能需要这样做。


2

2) 为什么我可以运行这段代码?它为什么不会崩溃?

这段代码从未引用未定义的内存或NULL。为什么它会崩溃呢?(尽管你的代码存在内存泄漏,但可能是因为你只展示了部分代码,在给定的程序中并不是问题。)

你提出的替代代码也可以工作,尽管从malloc返回的内存默认情况下也是未初始化的。(我曾经使用过一个自定义的内存分配器,默认情况下将返回的内存块填充为?字符。按照规则仍然完全合法。请注意,calloc返回指向零初始化内存的指针;如果需要这样的内存,请使用它。)

3) foo.b如何设置?它是否引用随机内存或NULL?

随机内存。对于在栈上分配的结构体,不会为您初始化。


foo.b的生命周期是否延伸到程序的整个执行过程中,还是随着函数的结束而结束?@DonalFellows - jdyg

1

你可以这样做,但不仅如此。

因为第二个字段是一个指针,必须设置为有效的地址。其中一种方法是通过分配内存(使用malloc)来完成。

对于你的第一个问题 - 仅当必须手动管理对象的生命周期时才使用malloc。

例如,第二段代码可以重写为:

int main(int argc, char* argv[])
{
 foo foo2; uint32 b;
 foo2.a = 3;
 foo2.b = &b;
 *foo2.b = 123;
}

这样更好,因为生命周期相同,内存现在在堆栈上 - 不需要释放。


1
结构体实例("foo2")的内存将被分配到堆栈上 - 没有必要自己分配内存 - 如果使用malloc进行分配,请确保稍后释放内存。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接