In a recent code review, it was claimed that
On select systems,
calloc()
can allocate more thanSIZE_MAX
total bytes whereasmalloc()
is limited.
My claim is that that's mistaken, because calloc()
creates space for an array of objects - which, being an array, is itself an object. And no object can be larger in size than SIZE_MAX
.
So which of us is correct? On a (possibly hypothetical) system with address space larger than the range of size_t
, is calloc()
allowed to succeed when called with arguments whose product is greater than SIZE_MAX
?
To make it more concrete: will the following program ever exit with a non-zero status?
#include <stdint.h> #include <stdlib.h> int main() { return calloc(SIZE_MAX, 2) != NULL; }
Can calloc() allocate more than SIZE_MAX in total?
As the assertion "On select systems, calloc()
can allocate more than SIZE_MAX
total bytes whereas malloc()
is limited." came from a comment I posted, I will explain my rationale.
size_t
size_t
is some unsigned type of at least 16 bits.
size_t
which is the unsigned integer type of the result of thesizeof
operator; C11dr §7.19 2"Its implementation-defined value shall be equal to or greater in magnitude ... than the corresponding value given below" ... limit of
size_t
SIZE_MAX
... 65535 §7.20.3 2
sizeof
The
sizeof
operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. §6.5.3.4 2
calloc
void *calloc(size_t nmemb, size_t size);
The
calloc
function allocates space for an array ofnmemb
objects, each of whosesize
is size. §7.22.3.2 2
Consider a situation where nmemb * size
well exceeds SIZE_MAX
.
size_t alot = SIZE_MAX/2; double *p = calloc(alot, sizeof *p); // assume `double` is 8 bytes.
If calloc()
truly allocated nmemb * size
bytes and if p != NULL
is true, what spec did this violate?
The size of each element, (each object) is representable.
// Nicely reports the size of a pointer and an element. printf("sizeof p:%zu, sizeof *p:%zu\n", sizeof p, sizeof *p);
Each element can be accessed.
// Nicely reports the value of an `element` and the address of the element for (size_t i = 0; i<alot; i++) { printf("value a[%zu]:%g, address:%p\n", i, p[i], (void*) &p[i]); }
calloc()
details
"space for an array of nmemb
objects": This is certainly a key point of contention. Does the "allocates space for the array" require <= SIZE_MAX
? I found nothing in the C spec to require this limit and so conclude:
calloc()
may allocate more thanSIZE_MAX
in total.
It is certainly uncommon for calloc()
with large arguments to return non-NULL
- compliant or not. Usually such allocations exceed memory available, so the issue is moot. The only case I've encountered was with the Huge memory model where size_t
was 16 bit and the object pointer was 32 bit.
SIZE_MAX
doesn't necessary specify the maximum size of an object, but rather the maximum value of size_t
, which is not necessarily the same thing. See Why is the maximum size of an array "too large"?,
But obviously, it isn't well-defined to pass a larger value than SIZE_MAX
to a function expecting a size_t
parameter. So in theory SIZE_MAX
is the limit, and in in theory calloc
would allow for SIZE_MAX * SIZE_MAX
bytes to allocated.
The thing with malloc
/calloc
is that they allocate objects without a type. Objects with a type have restrictions, such as never being larger than a certain limit like SIZE_MAX
. But the data pointed-at by the result from these functions does not have a type. It is not (yet) an array.
Formally, the data has no declared type, but as you store something inside the allocated data, it gets the effective type of the data access used for storage (C17 6.5 §6).
This in turn means that it would be possible for calloc
to allocate more memory than any type in C can hold, because what's allocated does not (yet) have a type.
Therefore, as far as the C standard is concerned, it is perfectly fine for calloc(SIZE_MAX, 2)
to return a value different from NULL. How to actually use that allocated memory in a sensible way, or which systems that even support such large chunks of memory on the heap, is another story.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With