David T. Ashley said:
It isn't immediately clear to me why the call has to fail.
2^16 * 2^16 is 2^32 (4 gigabytes). My system has more virtual
memory than that.
Well, just out of curiousity, I tried it out to see what the
largest approximate value is. Results below.
54135^2 is going to be on the order of 2.5G. That is a pretty
fair hunk of memory.
---------
[nouser@pamc ~]$ cat test3.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *p;
int i;
for (i=65535; i>0; i--)
{
if (p = calloc(i,i))
{
printf("%d is apparently the largest integer that will succeed.\n",
i);
break;
}
}
}
[nouser@pamc ~]$ gcc test3.c
[nouser@pamc ~]$ ./a.out
54135 is apparently the largest integer that will succeed.
[nouser@pamc ~]$
Watch out for system specific limits though.
For instance, on a system with a 32-bit size_t type, it may be in
theory possible to represent the size of a single object with a
size of SIZE_MAX, but user-specific limits might kick in a lot
earlier.
<OT>
For instance, on x86 systems the default installation of FreeBSD
shows:
| $ ulimit -a
| core file size (blocks, -c) unlimited
=> | data seg size (kbytes, -d) 524288
| file size (blocks, -f) unlimited
| max locked memory (kbytes, -l) unlimited
| max memory size (kbytes, -m) unlimited
| open files (-n) 7149
| pipe size (512 bytes, -p) 1
| stack size (kbytes, -s) 65536
| cpu time (seconds, -t) unlimited
| max user processes (-u) 3574
=> | virtual memory (kbytes, -v) unlimited
| $
Note, above, that a system-specific limit for the size of the
data segment of a single process, will prevent a successful
allocation of memory long before you hit the 4 GB limit of a
32-bit size_t.
You can probably use something like the following program:
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
size_t currsize, allocsize;
char *p, *tmp;
/*
* Try allocating memory by doubling the size in each step.
*/
allocsize = 1;
currsize = 0;
p = NULL;
while (1) {
if (currsize)
allocsize = 2 * currsize;
if ((SIZE_T_MAX / 2) < allocsize) {
fprintf(stderr,
"size_t limit would be exceeded!\n");
free(p);
exit(EXIT_SUCCESS);
}
printf("allocating %10ju", (uintmax_t) allocsize);
fflush(stdout);
if (p == NULL) {
tmp = malloc(allocsize);
} else {
tmp = realloc(p, allocsize);
}
if (tmp == NULL) {
printf("\nswitching algorithm at %ju bytes\n",
(uintmax_t) currsize);
fflush(stdout);
break;
}
p = tmp;
currsize = allocsize;
printf(" zeroing");
fflush(stdout);
memset(p, 0, currsize);
printf(" success\n", (uintmax_t) currsize);
fflush(stdout);
}
if (p == NULL || currsize == 0) {
fprintf(stderr, "Bummer! No allocation is possible.\n");
exit(EXIT_FAILURE);
}
/*
* Now try allocating repeatedly with 'allocsize', until we fail. When we
* do, decrease allocsize and loop back.
*/
allocsize = currsize;
while (1) {
if ((SIZE_T_MAX - allocsize) < currsize || allocsize < 1) {
printf("Cannot allocate any more memory.\n");
fflush(stdout);
break;
}
printf("allocating %10ju+%ju",
(uintmax_t) currsize, (uintmax_t) allocsize);
fflush(stdout);
assert(p != NULL);
tmp = realloc(p, currsize + allocsize);
if (tmp == NULL) {
allocsize /= 2;
printf(" failed, reducing allocsize to %ju\n",
(uintmax_t) allocsize);
continue;
}
p = tmp;
currsize += allocsize;
printf(" zeroing");
fflush(stdout);
memset(p, 0, currsize);
printf(" success\n", (uintmax_t) currsize);
fflush(stdout);
}
printf("Total memory allocated: %ju bytes\n", (uintmax_t) currsize);
fflush(stdout);
free(p);
return EXIT_SUCCESS;
}
When run with an unlimited virtual memory size user-limit, this
will succeed in allocating a *lot* of memory.
But see what happens when user-limits are in place:
| $ ulimit -v 20000
| $ ulimit -a
| core file size (blocks, -c) unlimited
| data seg size (kbytes, -d) 524288
| file size (blocks, -f) unlimited
| max locked memory (kbytes, -l) unlimited
| max memory size (kbytes, -m) unlimited
| open files (-n) 7149
| pipe size (512 bytes, -p) 1
| stack size (kbytes, -s) 65536
| cpu time (seconds, -t) unlimited
| max user processes (-u) 3574
| virtual memory (kbytes, -v) 20000
| $ ./foo
| allocating 1 zeroing success
| allocating 2 zeroing success
| allocating 4 zeroing success
| allocating 8 zeroing success
| allocating 16 zeroing success
| allocating 32 zeroing success
| allocating 64 zeroing success
| allocating 128 zeroing success
| allocating 256 zeroing success
| allocating 512 zeroing success
| allocating 1024 zeroing success
| allocating 2048 zeroing success
| allocating 4096 zeroing success
| allocating 8192 zeroing success
| allocating 16384 zeroing success
| allocating 32768 zeroing success
| allocating 65536 zeroing success
| allocating 131072 zeroing success
| allocating 262144 zeroing success
| allocating 524288 zeroing success
| allocating 1048576 zeroing success
| allocating 2097152 zeroing success
| allocating 4194304 zeroing success
| allocating 8388608
| switching algorithm at 4194304 bytes
| allocating 4194304+4194304 failed, reducing allocsize to 2097152
| allocating 4194304+2097152 failed, reducing allocsize to 1048576
| allocating 4194304+1048576 failed, reducing allocsize to 524288
| allocating 4194304+524288 failed, reducing allocsize to 262144
| allocating 4194304+262144 failed, reducing allocsize to 131072
| allocating 4194304+131072 failed, reducing allocsize to 65536
| allocating 4194304+65536 failed, reducing allocsize to 32768
| allocating 4194304+32768 failed, reducing allocsize to 16384
| allocating 4194304+16384 failed, reducing allocsize to 8192
| allocating 4194304+8192 failed, reducing allocsize to 4096
| allocating 4194304+4096 failed, reducing allocsize to 2048
| allocating 4194304+2048 failed, reducing allocsize to 1024
| allocating 4194304+1024 failed, reducing allocsize to 512
| allocating 4194304+512 failed, reducing allocsize to 256
| allocating 4194304+256 failed, reducing allocsize to 128
| allocating 4194304+128 failed, reducing allocsize to 64
| allocating 4194304+64 failed, reducing allocsize to 32
| allocating 4194304+32 failed, reducing allocsize to 16
| allocating 4194304+16 failed, reducing allocsize to 8
| allocating 4194304+8 failed, reducing allocsize to 4
| allocating 4194304+4 failed, reducing allocsize to 2
| allocating 4194304+2 failed, reducing allocsize to 1
| allocating 4194304+1 failed, reducing allocsize to 0
| Cannot allocate any more memory.
| Total memory allocated: 4194304 bytes
| $
</OT>
Back to more topical stuff: the size_t type can represent the
size of much much bigger objects, but local configuration
prevents malloc() and realloc() from obtaining so much memory.