0

I want to create an array with exactly UINT_MAX number of indexes. This has been extremely hard for some reason. I've tried the following:

  • 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

I don't understand what's going on. Is it my HEAP size too low? If so, how can I increase it? Can malloc/calloc not handle blocks of that nature? I've found nothing useful in the malloc or calloc API pages.

This question applies to both C and C++.

Windows 7 64 Bit, 16GB RAM, CMake 3.6.1, GCC 7.11.1, and compiled on CLion 64 bit.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
Hatefiend
  • 3,416
  • 6
  • 33
  • 74
  • 32 bits? or 64? You are asking for 4GB of memory, which obviously won't fly if you only have 32 bits of address space. – Marc Glisse Sep 28 '17 at 10:44
  • 1
    have you tried sth like: int x = (int) UINT_MAX; char arr[x]; – Kev1n91 Sep 28 '17 at 10:46
  • 2
    `UINT_MAX` typically exceeds all your available RAM. Good luck with that. – Sam Varshavchik Sep 28 '17 at 10:46
  • 1
    @SamVarshavchik Only if you still live in 1995... – Maxim Egorushkin Sep 28 '17 at 10:47
  • 1
    Memory size is expressed using the type `size_t`. What is the backing type for `size_t` on your system? How does `size_t`compare to the type of `UINT_MAX`? – Klas Lindbäck Sep 28 '17 at 10:51
  • No, it doesn't apply to both C and C++. Neither of your attempts at dynamic allocation is a valid C++ statement. – StoryTeller - Unslander Monica Sep 28 '17 at 10:52
  • 2
    There is no gcc 7.11.1. And I think your IDE is configured to compile 32 bit programs, check the settings. – Marc Glisse Sep 28 '17 at 10:52
  • How big is `unsigned int`, if it's 64 bit, `UINT_MAX` is likely to exceed all the RAM on Earth. – JeremyP Sep 28 '17 at 10:54
  • https://stackoverflow.com/q/33923609/1918193 may be related. – Marc Glisse Sep 28 '17 at 10:55
  • 3
    Calloc is returning NULL too and you just aren't checking it. Your system's simply refusing t give you UINT_MAX bytes of continuous virtual memory. There's hardly anything your program can do about it. – Petr Skocik Sep 28 '17 at 10:56
  • `size_t` is almost always the same size as an `unsigned int`. On my system, both show as `4` bytes during runtime. – Hatefiend Sep 28 '17 at 10:57
  • @JeremyP unlikely though! that would be only 4 bln devices with 4G ram. – Antti Haapala -- Слава Україні Sep 28 '17 at 10:57
  • 2
    You question lacks something essential. What motivates such a large allocation? So **edit your question** to improve it a lot (by telling more: what kind of application, how many RAM do you have, what is that array used for, and **why**? etc). You need to add several paragraphs. BTW both `calloc` and `malloc` are documented well, and they can fail (and probably do). – Basile Starynkevitch Sep 28 '17 at 10:58
  • 1
    Please post a complete example with the actual error message. I don't think that the compiler would say that "array size cannot be negative". – interjay Sep 28 '17 at 10:59
  • 1
    @JeremyP `UINT_MAX` shows as `4294967295`. This means I'm asking for `4,294,967,295` bytes. That's only `4GB` of RAM. – Hatefiend Sep 28 '17 at 10:59
  • 1
    @BasileStarynkevitch I wrote above my system specifications. As to why I want to make an array of this size, that's irrelevant. It's more of a question of how can it be done. – Hatefiend Sep 28 '17 at 11:00
  • 1
    Only 4Gb! That's still quite a lot to allocate in one chunk. The seg fault is probably because the C library you are using can't handle it. – JeremyP Sep 28 '17 at 11:01
  • 1
    It is relevant because very likely some [XY problem](http://xyproblem.info). And it can be done but requires careful coding. Your question lacks an [MCVE](https://stackoverflow.com/help/mcve) – Basile Starynkevitch Sep 28 '17 at 11:02
  • 2
    @Hatefiend *"`size_t` is almost always the same size as an `unsigned int`"* is total bs. They're **never** the same size on modern 64-bit PC systems. – Antti Haapala -- Слава Україні Sep 28 '17 at 11:02
  • 1
    @Hatefiend you must produce a [mcve], and copy paste the error messages, and check the return value of `malloc`; `calloc` and print the error messages using `perror`, and check the `gcc` version number as the gcc is not in version `7.11.x` yet, the latest being `7.2`, therefore either you're a time traveller or your question is wrong. – Antti Haapala -- Слава Україні Sep 28 '17 at 11:09

3 Answers3

5

First, let me point out is that you can and should use errno to check why API calls fail.

Now, Let's examine your tries:

stack allocation - char arr[UINT_MAX]

You are trying to allocate 4294967295 bytes on your stack, which is not feasible, since operating systems limit the stack size to much smaller sizes. You could try and manipulate it using the APIs specified in this manual.

malloc

A heap allocation will fail in most cases for a single reason: The operating system couldn't allocate a big enough contiguous memory.

It's not that your system doesn't have 4GB of free memory, but it is entirely possible that there no single chunk of contiguous memory that suits your request. So, just like the malloc's MSDN page points out:

malloc returns a void pointer to the allocated space, or NULL if there is insufficient memory available

calloc

malloc and calloc use the same underlying mechanisms, so if one fails, you should expect the second one to fail as well. See remarks section of calloc's MSDN page


Interesting facts about malloc on Linux platforms

Following requests from the comments, I will point out some interesting facts about malloc's implementation on Linux, since it differs significantly!

First of all, Linux specifically specifies it uses an optimistic memory management algorithm:

By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available. In case it turns out that the system is out of memory, one or more processes will be killed by the OOM killer

This means that the memory is not actually owned and dedicated for the callee, but rather the operating system hopes that the memory will be available for him when he needs it, and if it isn't, it reverts to some nasty means.

Edit: As @Basile Starynkevitch correctly pointed out, this mechanism depends on the memory overcommitment switch, which is enabled by default, but can be disabled.

Daniel Trugman
  • 8,186
  • 20
  • 41
  • Note, if `malloc()` does not return `NULL` it does not mean there is enough memory. Quote from `man malloc`: "By default, Linux follows an optimistic memory allocation strategy. This means that when `malloc()` returns non-`NULL` there is no guarantee that the memory really is available." – Kane Sep 28 '17 at 11:40
  • @Kane, this question is regarding windows, not linux, and malloc is implementation defined. – Daniel Trugman Sep 28 '17 at 11:41
  • I mean that the answer IMO would benefit if you would mention "implementation defined"-ness of `malloc()` there. – Kane Sep 28 '17 at 11:47
  • 1
    What you say about `malloc` on Linux hold only if memory overcommitment is enabled (which it is by default); I am usually disabling it. – Basile Starynkevitch Sep 28 '17 at 13:23
3

It is operating system specific. But not checking against failure of malloc or calloc leads to undefined behavior (e.g. your segfault with calloc) and is always wrong (and a basic mistake you should avoid doing). Remember that malloc and calloc can fail, and are more likely to fail when you request or have already used a lot of memory (because virtual memory is a limited resource). Notice that sizeof(char) is always 1.

The following program

 // 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=%zd\n", 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: %s\n", cmd);
   fflush(NULL);
   int bad = system(cmd);
   if (bad) return  EXIT_FAILURE;
   return 0;
 }   

works well on my Linux/Debian/x86-64 laptop system with 16 Gbytes RAM (and won't be optimized by gcc -Wall -O hatefiend.c -o hatefiend removing ad because it is used in printf and filled) giving:

% ./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

as you can see from the output of pmap(1) the memory is indeed allocated.

and you could easily adapt it for your Windows system: remove <unistd.h> and change the snprintf to build the command showing the virtual address space, i.e. replace pmap by some Windows specific utility; or just remove all the building of the command i.e. cmd and its usage...

So do yourself a favor: install and use Linux (that could be seen as a provocation or as a useful hint). It is likely that on Windows your malloc or calloc has failed. Or work hard to understand why did that happen, and perhaps configure something in your system to let a huge malloc succeeds. How to do that is a sysadmin issue, out of scope on Stack Overflow (but you could ask on SuperUser). Beware of memory overcommitment (I usually disable that).

Is it my HEAP size too low? If so, how can I increase it?

You probably want to increase the virtual memory or the virtual address space limit (since heap memory is what malloc gives). How to do that is a sysadmin issue (should be asked on SuperUser), and you'll need to give a lot more details to get help.

In a comment you mentioned that your size_t is a 32 bits integer (which surprises me a lot; are you compiling in 64 bits mode? on my Linux/x86-64 machine it is 64 bits, like some uint64_t or unsigned long). Then I believe that what you want is impossible on your system (because malloc implementation would need some extra space -e.g. a few extra words for book keeping- and perhaps counts that overhead in some internal size_t ....)


Remember that this malloc (not very useful, but very quick) is following the letter of the C standard.


However I do believe that you have some XY problem, and you could achieve your goals (which are unstated) otherwise. This is why motivating questions is important on Stack Overflow. Maybe declaring arr as a static variable might help: static char arr[UINT_MAX];.... but read also this for the disadvantages.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

Not sure why you need that much char array, but anyway

char arr[UINT_MAX];

That's too huge for stack size.. No compilers allocate this much stack size. you can increase the stack size while compiling with flag -stack but its still limited to the your system configurations.

you can check your limits by invoking command ulimit -s and set it to unlimited ulimit -s unlimited before trying that..However it can still fail.

malloc returns NULL as it can't allocate the memory you asked for and same for calloc.

Increase you virtual memory or physical memory or both to get this memory. Even if memory is available, it may well be fragmented to a extent its not possible for system to honour your request.

Daksh Gupta
  • 7,554
  • 2
  • 25
  • 36
  • 3
    *`char arr[UINT_MAX];` That's too huge for stack size* Not for a 64-bit process. – Andrew Henle Sep 28 '17 at 10:59
  • What if I requested small bits of the array and had it separated into chunks. Similar to a 2D array except every sub array is in a completely different location in memory? – Hatefiend Sep 28 '17 at 11:06
  • 1
    @AndrewHenle `UINT_MAX` is definitely larger than the stack-grow-zone below the currently used stack, where accesses to unallocated pages will trigger the kernel to grow the memory mapping that's backing the stack instead of killing the process with a segfault. If you allocate 4 GiB of stack space, and start writing to it, your memory access will be 4 GiB far away from the stack memory region that the kernel knows about, and thus your process will be shot with a segfault. *You cannot expect to be able to allocate more than a mebibyte of stack-space at a time*. – cmaster - reinstate monica Sep 28 '17 at 11:12
  • @cmaster That could be interpreted as a bug in the OS. – Andrew Henle Sep 28 '17 at 11:30
  • Stack will be defined by platform and toolchain, e.g. https://stackoverflow.com/a/1825975/1462295 and see other answers there. c.f. https://stackoverflow.com/q/29960754/1462295 and https://stackoverflow.com/q/2275550/1462295 etc – BurnsBA Sep 28 '17 at 12:06
  • 1
    @AndrewHenle https://stackoverflow.com/q/1825964/2445184 There is no right to infinite stack space formulated within the C/C++ standards. So, definitely not an OS bug, just a platform specific restriction. – cmaster - reinstate monica Sep 28 '17 at 13:16