当“char bigchar [1u<< 31 - 1];”时,我为什么要使用malloc()工作得很好?

时间:2009-05-08 10:51:44

标签: c arrays static malloc

在静态数组上使用malloc(除了NULL返回失败)有什么好处?以下程序将占用我所有的ram并且只有在取消注释循环时才开始填充交换。它没有崩溃。

...

#include <stdio.h>

unsigned int bigint[ 1u << 29 - 1 ];
unsigned char bigchar[ 1u << 31 - 1 ];

int main (int argc, char **argv) {
  int i;
/*   for (i = 0; i < 1u << 29 - 1; i++) bigint[i] = i; */
/*   for (i = 0; i < 1u << 31 - 1; i++) bigchar[i] = i & 0xFF; */

  getchar();
  return 0;
}

...

经过一些试验和错误后,我发现上面是我的32位Intel机器上允许使用GCC 4.3的最大静态阵列。这是标准限制,编译器限制还是机器限制?显然我可以拥有我想要的那么多。这将是段错误,但只有当我要求(并尝试使用)时,malloc才会给予我更多。

有没有办法确定静态数组是否实际分配并且可以安全使用?

编辑:我对为什么使用malloc来管理堆而不是让虚拟内存系统处理它感兴趣。显然,我可以将一个数组的大小调整到我认为我需要的大小的很多倍,并且虚拟内存系统只会保留ram所需的内容。如果我从未写过例如这些巨大阵列的结束(或开始)然后程序不使用物理内存。此外,如果我可以写入每个位置,那么除了在堆中递增指针或在同一进程中搜索先前的分配之外,malloc还会做什么呢?

编者注: 1 << 31 causes undefined behaviour如果int是32位,那么我已将问题修改为1u。问题的目的是询问有关分配大型静态缓冲区的信息。

8 个答案:

答案 0 :(得分:14)

嗯,真的有两个原因:

  1. 由于可移植性,因为某些系统不会为您进行虚拟内存管理。

  2. 你不可避免地需要将这个数组划分为更小的块以使其有用,然后跟踪所有块,然后最终当你开始“释放”数组的某些块时你没有更长的时间要求你遇到memory fragmentation的问题。

  3. 总而言之,你最终会实现大量的内存管理功能(实际上几乎重新实现了malloc)而没有可移植性的好处。

    因此原因:

    • 通过内存管理封装和标准化实现代码可移植性。

    • 通过代码重用的方式提高个人工作效率。

答案 1 :(得分:11)

答案 2 :(得分:10)

使用malloc,您可以增长和缩小数组:它变得动态,因此您可以完全根据需要进行分配。

答案 3 :(得分:5)

我猜这叫做自定义内存管理。 你可以这样做,但你必须自己管理那块内存。 你最终会编写自己的malloc()来解决这个问题。

答案 4 :(得分:2)

关于:

  

经过一番试验和错误,我找到了   上面是最大的静态数组   在我的32位Intel机器上允许   与GCC 4.3。这是标准吗?   限制,编译器限制或机器   限制?

一个上限取决于4GB(32位)虚拟地址空间如何在用户空间和内核空间之间进行分区。对于Linux,我认为最常见的分区方案具有3 GB的用户空间地址范围和1 GB的内核空间地址范围。分区可在内核构建时配置,2GB / 2GB和1GB / 3GB分割也在使用中。加载可执行文件时,必须为每个对象分配虚拟地址空间,而不管是否分配了实内存来备份它。

答案 5 :(得分:2)

您可以在一个上下文中分配该巨大的数组,但不能在其他上下文中分配。例如,如果您的数组是结构的成员,并且您希望传递结构。某些环境对结构大小有32K的限制。

如前所述,您还可以调整内存大小以准确使用所需内容。在性能关键的上下文中,如果可以避免,则不要分页到虚拟内存。

答案 6 :(得分:2)

除了超出范围之外,没有办法释放堆栈分配。因此,当您实际使用全局分配并且VM必须为您分配实际硬内存时,它将被分配并保留在那里直到您的程序用完为止。这意味着任何进程只会在其虚拟内存使用中增长(函数具有本地堆栈分配,并且那些将被“释放”)。

一旦超出功能范围,就无法“保留”堆栈内存,它总是被释放。因此,您必须知道在编译时将使用多少内存。

然后归结为你可以拥有多少int foo [1&lt;&lt; 29]。由于第一个占用整个内存(在32位上)并且将(让谎言:0x000000)第二个将解析为0xffffffff或其他。然后第三个会解决什么? 32位指针无法表达的东西。 (请记住,堆栈预留部分在编译时解决,部分运行时,通过偏移,在分配此变量或变量时推送堆栈偏移量。)

所以答案很明显,一旦你有了int foo [1&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; 29],你就不能再使用其他本地堆栈变量进行任何合理的函数深度了。

答案 7 :(得分:1)

除非你知道自己在做什么,否则你应该避免这样做。尽量只根据需要请求尽可能多的内存。即使它没有被使用或妨碍其他程序,它也可能使这个过程陷入困境。有两个原因。首先,在某些系统上,尤其是32位系统,在极少数情况下会导致地址空间过早耗尽。此外,许多内核对保留/虚拟/未使用内存具有某种进程限制。如果程序在运行时点请求内存,如果内核要求保留超过此限制的内存,则内核可以终止该进程。我已经看到由于malloc失败而导致崩溃或退出的程序,因为它们仅使用几MB就保留了GB的内存。