2

The C specification says this about the memory allocation functions:

If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.

Do all the allocation functions have to be consistent in this regard? Can malloc(0) return a non-null pointer, while realloc(someptr, 0) returns a null pointer?

Can calloc(0, 0) be different from malloc(0)? It would have been simple for the calloc() specification to say that calloc(nmemb, size) is equivalent to malloc(nmemb * size) followed by zeroing the memory, but this equivalence is not explicit.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Just curious.. Is there any practical implication of either possibility? – Eugene Sh. Oct 18 '19 at 17:03
  • **Can `calloc(0, 0)` be different from `malloc(0)`?** Well, it _is_ implementation defined, and there is nothing to say that it _can't_ be different. – Ian Abbott Oct 18 '19 at 17:07
  • 3
    As for *to say that calloc(nmemb, size) is equivalent to malloc(nmemb * size) followed by zeroing the memory* - it is not always equivalent form implementation perspective. `calloc` might be implemented by OS allocating a zero-page, where applicable and not explicitly clearing out the one that the `malloc` would allocate. That is these two may use different heap pools. – Eugene Sh. Oct 18 '19 at 17:07
  • 2
    Regarding `calloc(nmemb, size)`, it could, in principle, be used to allocate a block larger than `SIZE_MAX`. – Ian Abbott Oct 18 '19 at 17:10
  • @IanAbbott: I'm not convinced that is the case. calloc can be used to *request* an allocation larger than `SIZE_MAX` but since no object can have that size, I think the request should be denied (although we know that many implementations simply let the product overflow, which means they effectively consider an oversized request to be UB). – rici Oct 18 '19 at 17:13
  • @rici "since no object can have that size" --> a memory allocation is not the same as defining an object. – chux - Reinstate Monica Oct 18 '19 at 17:16
  • @chux: "The malloc function allocates space for **an object** whose size is specified by size and whose value is indeterminate." (emphasis added) `calloc` reserves space for an "array of objects", but an array is also an object. – rici Oct 18 '19 at 17:19
  • @rici: "The `calloc` function allocates space for an array of `nmemb` objects, each of whose size is `size`." It's not really an _array_ though (you can't determine its size with `sizeof`), it is _allocated space_. – Ian Abbott Oct 18 '19 at 17:23
  • @rici Fair point, yet where does "since no object can have that size"` derive from? A _declared_ object is limited to `SIZE_MAX` to satisfy the `sizeof` operator. An allocated object lacks the same restriction. See. [Can calloc() allocate more than SIZE_MAX in total?](https://stackoverflow.com/questions/52699574/can-calloc-allocate-more-than-size-max-in-total) – chux - Reinstate Monica Oct 18 '19 at 17:23
  • The C standard says that about the memory allocation functions generally. So I would interpret that as there is one implementation-defined behavior for all of them. If the behavior could vary, as long as in each case it was always either a null pointer or some space not to be accessed, then it could not only vary by function but by individual call or day of the week. One behavior for all functions in the implementation is the better interpretation. (Except for the fact that incompletely defined behavior that varies by day of the week is preferable, since it causes earlier discovery of bugs.) – Eric Postpischil Oct 18 '19 at 17:26
  • @EricPostpischil Not normative, but J.13.12 says "Whether the `calloc`, `malloc`, and `realloc` functions return a null pointer or a pointer to an allocated object when the size requested is zero (7.22.3)." That _seems_ to rule out day of the week being a factor, as it seems to imply the behavior is fixed, at least at the per-function level. – Ian Abbott Oct 18 '19 at 17:30
  • @eric Agree about "better" yet C allows variant behavior on like functions in corner cases. Example: `atol()` "Except for the behavior on error, they are equivalent to" `strtol()` . Defensive coding would not assume a C spec for consistency here in `*alloc()`. – chux - Reinstate Monica Oct 18 '19 at 17:34
  • A difference of `malloc(0), realloc(someptr, 0)` could occur when resources (memory, memory handles, etc.) are scant. `malloc(0)` can easily return `NULL`, yet `realloc(someptr, 0)` could return `someptr` as doing its internal `free(someptr)` may be problematic. – chux - Reinstate Monica Oct 18 '19 at 17:39
  • @chux: But shouldn't I be able to write `char (*foo)[N][16] = calloc(16, size (*foo)[0]);` and then later use `sizeof *foo`? (Assume `N` is an integer constant expression.) gcc doesn't allow `char (*foo)[SIZE_MAX][16];`, which seems reasonable to me. – rici Oct 18 '19 at 17:45
  • @chux: However, I yield the point that I misremembered the requirement that `size_t` be big enough to represent the size of any object. I thought it was explicit. – rici Oct 18 '19 at 17:46
  • @rici In the days of `SIZE_MAX=65535` and memory pools > `SIZE_MAX` this `calloc(big, big)` came up. I see it happening again with `SIZE_MAX=4G`. It certainly is easier, and IMO less error prone, to fail `calloc(big, big)` for precisely even season programmers (happy 50th) find support of such huge objects _quirky_. – chux - Reinstate Monica Oct 18 '19 at 17:52
  • @rici: Yes it is reasonable for gcc to disallow `char (*foo)[SIZE_MAX][16]` because the type of `*foo` would be `char [SIZE_MAX][16]`, and `sizeof(char[SIZE_MAX][16])` is not representable by `size_t`. That has nothing to do with `calloc`. – Ian Abbott Oct 18 '19 at 18:11
  • @chux Your `realloc()` example is good. `realloc()` could easily start with `if (size <= oldsize) return ptr;` which would return a non-null pointer for `newsize=0`, regardless of how `malloc(0)` works. – Barmar Oct 18 '19 at 18:31
  • 1
    @EugeneSh. Probably little practical use, since a non-null pointer returned can't be dereferenced in any case. It's a curious implementation freedom, which was prompted by investigating [this question](https://stackoverflow.com/questions/58442295/why-does-calloc-allocate-1-byte-if-nelem-or-elsize-zero) – Barmar Oct 18 '19 at 18:33
  • @IanAbbott: Perhaps we can agree that the standard is badly specified. To my mind, if there is no type for any array which would occupy the storage allocated by `calloc`, then you cannot talk about that array. The correct formulation might be something like "The `calloc` function allocates space for *`nmemb` consecutive objects*, each of whose size is `size`." But on the whole I think it would be better if the standard either said that it is UB or implementation-defined to call to `calloc` with `nmemb * size` is greater than `SIZE_MAX`, or required the result to be NULL. – rici Oct 18 '19 at 19:16
  • Is there any need that the "implementation defined" behaviour has to be equal on all these functions? Since there has to be a difinition in the implementation, it could define different behaviour for each function. – the busybee Oct 19 '19 at 08:59
  • @rici I agree the use of the word _array_ is confusing. On the other hand, if there was no need for the possibility of allowing `calloc` to allocate memory larger than `SIZE_MAX`, there would be no need for it to have two parameters. – Ian Abbott Oct 21 '19 at 09:37

0 Answers0