我想创建一个具有UINT_MAX
索引数的数组。由于某种原因,这非常困难。我尝试了以下内容:
-
char arr[UINT_MAX]; // Compiler claims array size cannot be negative
-
char* const arr = calloc(UINT_MAX, sizeof(char)); // Runs but seg fault when accessed
-
char* const arr = malloc(sizeof(char) * UINT_MAX); // arr is NULL
我不明白发生了什么。这是我的堆尺寸太低吗?如果是这样,我该如何增加它?malloc/calloc
不能处理这种性质的块吗?我发现在malloc
或calloc
API页面中没有任何有用。
这个问题同时适用于C 。
Windows 7 64位,16GB RAM,CMAKE 3.6.1,GCC 7.11.1,并在Clion 64位上编译。
首先,让我们首先我指出的是,您可以和应该使用errno
检查为什么API调用失败。
现在,让我们检查一下您的尝试:
堆栈分配 -char arr[UINT_MAX]
您正在尝试在堆栈上分配4294967295字节,这是不可行的,因为操作系统将堆栈尺寸限制为较小的尺寸。您可以尝试使用本手册中指定的API进行操作。
malloc
在大多数情况下,堆分配将失败,原因是:操作系统无法分配足够大的连续内存。
并不是您的系统没有4GB的免费内存,而是完全不适合您请求的单个连续内存。因此,就像malloc
的MSDN页面指出:
malloc
返回一个空隙指针到分配的空间,如果没有足够的内存,则null
calloc
malloc
和calloc
使用相同的基础机制,因此,如果失败,您应该期望第二个机制也会失败。请参阅calloc
的MSDN页面的备注部分
关于malloc
在Linux平台上的有趣事实
按照评论的请求,我将指出有关malloc
在Linux上实现的一些有趣事实,因为它有很大不同!
首先,Linux专门指定其使用乐观的内存管理算法:
默认情况下,Linux遵循一个乐观的内存分配策略。这意味着,当malloc()返回非零件时,无法保证内存确实可用。如果事实证明该系统不在记忆中,那么一个或多个过程将被OOM Killer
杀死
这意味着内存实际上不是为Callee拥有和专用的,而是操作系统希望在他需要的时候可以为他提供内存,如果不是,它恢复了一些令人讨厌的手段。
edit :正如@basile starynkevitch正确指出的那样,此机制取决于内存过度的开关,默认情况下启用了, 但可以禁用。
它是特定于操作系统的。但是,不检查malloc
或calloc
的故障会导致不确定的行为(例如,您使用calloc
的SEGFAULT),并且是始终错误(并且应该避免这样做的基本错误)。请记住,malloc
和calloc
可能会失败,并且在请求或已经使用了大量内存时更有可能失败(因为虚拟内存是有限的资源)。请注意,sizeof(char)
总是1。
以下程序
// file hatefiend.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
int main(int argc, char**argv) {
size_t sz = (argc>1)?(size_t)(atol(argv[1])):UINT_MAX;
char *ad = malloc(sz);
if (!ad) {perror("malloc"); exit(EXIT_FAILURE); };
for (unsigned long ix=0; ix<(unsigned long)sz; ix += 1024)
ad[ix] = (char)(' ' + (ix & 0x3f));
printf ("ad=%p sz=%zdn", ad, sz);
/// the code below is optional and Linux specific,
/// remove it or adapt it for Windows
char cmd[80];
snprintf(cmd, sizeof(cmd), "pmap %d", (int)getpid());
printf ("command: %sn", cmd);
fflush(NULL);
int bad = system(cmd);
if (bad) return EXIT_FAILURE;
return 0;
}
在我的Linux/debian/x86-64笔记本电脑系统上使用16 gbytes RAM(并且不会通过gcc -Wall -O hatefiend.c -o hatefiend
优化ad
,因为它在printf
中使用并填充)给予:
% ./hatefiend
ad=0x7fc3d5c09010 sz=4294967295
command: pmap 31644
31644: ./hatefiend
000056355b8e3000 4K r-x-- hatefiend
000056355bae3000 4K r---- hatefiend
000056355bae4000 4K rw--- hatefiend
000056355c3de000 132K rw--- [ anon ]
00007fc3d5c09000 4194308K rw--- [ anon ]
00007fc4d5c0a000 1612K r-x-- libc-2.24.so
00007fc4d5d9d000 2048K ----- libc-2.24.so
00007fc4d5f9d000 16K r---- libc-2.24.so
00007fc4d5fa1000 8K rw--- libc-2.24.so
00007fc4d5fa3000 16K rw--- [ anon ]
00007fc4d5fa7000 140K r-x-- ld-2.24.so
00007fc4d6190000 8K rw--- [ anon ]
00007fc4d61c7000 12K rw--- [ anon ]
00007fc4d61ca000 4K r---- ld-2.24.so
00007fc4d61cb000 4K rw--- ld-2.24.so
00007fc4d61cc000 4K rw--- [ anon ]
00007ffe8f561000 132K rw--- [ stack ]
00007ffe8f5b7000 12K r---- [ anon ]
00007ffe8f5ba000 8K r-x-- [ anon ]
total 4198476K
您可以从PMAP的输出(1)看到内存确实分配了。
您可以轻松地适应Windows系统:删除<unistd.h>
并更改snprintf
以构建显示虚拟地址空间的命令,即用某些Windows特定实用程序替换pmap
;或只需删除命令的所有构建,即cmd
及其用法...
所以请自己帮个忙:安装和使用Linux(可以将其视为挑衅或有用的提示)。 Windows 上的malloc
或calloc
上的可能已经失败了。或努力工作以了解为什么会发生这种情况,也许在系统中配置某些内容让巨大的malloc
成功。如何做到这是一个sysadmin问题,出于堆栈溢出的范围(但是您可以询问超级用户)。当心内存过度承诺(我通常会禁用)。
我的堆尺寸太低了吗?如果是这样,我该如何增加它?
您可能想增加虚拟内存或虚拟地址空间限制(因为堆是malloc
给出的)。如何做到这是一个sysadmin问题(应该在Superuser上问),您需要提供更多的详细信息才能获得帮助。
在评论中,您提到您的size_t
是32位整数(这让我感到惊讶; 您是否以64位模式编译?我的linux/x86-64机器是64位,例如某些uint64_t
或unsigned long
)。然后,我相信您想要的系统是不可能的(因为malloc
实现将需要一些额外的空间-e.g。书籍保存的几个额外单词,也许在某些内部size_t
中算出开销。...)
请记住,此malloc(不是很有用,但很快)遵循C标准的字母。
但是,我确实相信您有一些XY问题,否则您可以实现自己的目标(这是未说的)。这就是为什么激励问题在堆栈溢出中很重要的原因。也许将arr
声明为静态变量可能会有所帮助:static char arr[UINT_MAX];
....但还请阅读此内容。数组,但是无论如何
char arr[UINT_MAX];
这太大了,对于堆栈尺寸而言。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。您可以在使用FLAG -stack
编译时增加堆栈尺寸,但仍限于您的系统配置。
您可以通过调用命令ulimit -s
检查限制,然后将其设置为无限制的ulimit -s unlimited
。
malloc
返回null,因为它不能分配您要求的内存,而对于calloc
。
增加您的虚拟内存或物理内存或两者都能获得此内存。即使有可用的内存,它也很可能在某种程度上分散了系统来纪念您的请求。