我正在尝试取消 C 函数malloc
和free
。我知道这在StackOverflow上已经讨论了很多。但是,我想我现在有点知道这些功能的作用了。我想知道为什么要使用它们。让我们看一下这段代码:
int n = 10;
char* array;
array = (char*) malloc(n * sizeof(char));
// Check whether memory could be allocated or not...
// Do whatever with array...
free(array);
array = NULL;
我创建了一个 char 类型的指针,我称之为 array
。然后,我使用 malloc
查找当前未使用的内存块,并且(10 * sizeof(char))
字节大。在将其分配给我之前创建的字符指针之前,我强制转换为键入字符指针的地址。现在我可以使用我的字符数组。完成后,我将使用 free
来释放该内存块,因为它不再被使用。
有一个问题:我为什么不做char array[10];
?维基百科只有一小句话可以回答这个问题,不幸的是,这句话我不明白:
但是,数组的大小在编译时是固定的。如果希望动态分配类似的数组...
我大学的幻灯片同样简洁明了:
也可以从堆中分配内存。
什么是堆?我知道一种叫做堆的数据结构。:)
但是,我有人可以向我解释,在这种情况下,使用 malloc
和 free
而不是变量的常规声明是有意义的,那就太好了。 :)
C 为对象提供了三种不同的可能"存储持续时间":
-
自动 - 特定于其所在函数的调用的本地存储。如果以递归方式或从多个线程调用函数,则使用自动存储创建的对象可能有多个实例。或者可能没有实例(如果/当函数未被调用时)。
-
静态 - 在正在运行的程序的整个持续时间内只存在于一个实例中的存储。
-
已分配(动态) - 由
malloc
创建,并一直持续到调用free
以释放它或程序终止。分配存储是唯一可以创建任意大或任意多个对象的存储类型,即使函数返回也可以保留这些对象。这就是malloc
的用处。
首先,没有必要投malloc
array = malloc(n * sizeof(char));
我有一个问题:我为什么不做
char array[10];
?
如果您不知道想要多少存储空间(例如,如果您想拥有任意大小的数组,例如堆栈或链表),您会怎么做?
在这种情况下,您必须依赖malloc
(在 C99 中,您可以使用可变长度数组,但内存大小较小)。
函数malloc
用于在程序执行期间分配一定量的内存。malloc
函数将从堆中请求内存块。如果请求获得批准,操作系统将保留请求的内存量。
当不再需要内存量时,必须通过调用函数 free
将其返回到操作系统。
简单来说:当您知道数组在编译时需要容纳的元素数时,您可以使用数组。 当您不知道数组在编译时需要多少个元素时,您可以将 malloc 与指针一起使用。
有关更多详细信息,请阅读使用 malloc()
和 free()
进行堆管理 。
假设您要分配 1,000 个数组。
如果你没有malloc和免费...但是需要在每个数组的源代码中声明,那么您必须进行 1,000 个声明。 你必须给他们所有的名字。 ( array1
, array2
, ... array1000
)。
动态内存管理的一般思想是在编写程序时无法提前知道项目数量时处理项目。
关于你的问题:
我为什么不只做字符数组[10];?。 你可以,而且大多数时候,这将完全足够了。 但是,如果您想做类似但更大的事情怎么办? 或者,如果数据大小在执行过程中需要更改,该怎么办? 这些是指向使用动态分配的内存(calloc()
或malloc()
)的一些情况。
了解一下堆栈和堆的使用方式/时间
会很好:当你使用 malloc()
或 calloc()
时,它使用堆中的内存,其中自动/静态变量在堆栈上被赋予内存,并在您离开该变量的作用域(即声明它的函数或块)时释放。
当所需数据的大小直到运行时才知道时,使用 malloc 和 calloc 变得非常有用。确定大小后,您可以轻松地调用其中一个来将内存分配到堆上,然后在完成后,使用free()
释放它
关于什么是堆?这里有一个关于这个话题的很好的讨论(主题略有不同,但讨论得很好)
作为回应 但是,有人可以向我解释在这种情况下使用 malloc()
和 free()
是有意义的......?
简而言之,如果您知道特定变量在构建时(运行时之前)的内存要求,请使用静态/自动创建变量(以及相应的内存使用情况)。 如果在运行时之前不知道需要什么大小,请使用 malloc()
或 calloc()
与相应的调用 free()
(每次使用)来创建内存。 这当然是一个经验法则,也是一个粗略的概括。 随着使用内存经验的积累,您会发现即使在运行时之前知道大小信息的情况,您也会选择动态分配,因为某些其他条件。 (想到大小)
如果你事先知道你只需要一个 10 个字符的数组,你应该说char array[10]
. 如果您事先不知道需要多少存储空间,Malloc 很有用。 如果您需要在当前函数返回后有效的存储,它也很有用。 如果将数组声明为 char array[10]
,它将在堆栈上分配。函数返回后,此数据将无效。 从malloc
获取的存储在您对其调用free
之前一直有效。
此外,无需强制转换 malloc 的返回值。
为什么在 malloc 之后使用 free 可以理解为,一旦不需要就释放内存是一种很好的风格。但是,如果您不释放内存,那么它不会造成太大损害,但只会增加您的运行时成本。
您也可以选择在退出程序时保持未释放内存。 malloc() 使用堆,当进程退出时,进程的完整堆被释放。人们坚持释放内存的唯一原因是为了避免内存泄漏。
从这里:
分配误区4:非垃圾回收程序应始终 释放它们分配的所有内存。
真相:频繁执行的代码中省略了解除分配导致 泄漏越来越多。它们很少被接受。但保留的程序 在程序退出之前,大多数分配的内存通常执行得更好,没有 任何干预性释放。Malloc 更容易实现,如果 没有免费的。
在大多数情况下,在程序退出之前解除分配内存 毫无 意义。操作系统无论如何都会回收它。自由将触摸和翻页 死物;操作系统不会。
后果:小心计算分配的"检漏仪"。 有些"泄漏"是好的!
此外,维基在堆基内存分配方面有一个很好的观点:-
堆方法存在一些固有缺陷,完全源于 从碎片化。与任何内存分配方法一样,堆 将变得支离破碎;也就是说,将有部分使用和 堆上分配的空间中未使用的内存。一个好的分配器 将尝试查找已分配内存的未使用区域以供使用 在诉诸扩展堆之前。这样做的主要问题 方法是堆只有两个重要属性:base 或 虚拟内存空间中堆的开头;和长度,或其 大小。堆需要足够的系统内存来填充其整个 长度,其底座永远不会改变。因此,任何大面积的未使用 内存被浪费了。如果 堆的末尾存在小的已用段,这可能会浪费 地址空间的任何大小,从几兆字节到几百兆字节。