Am Sat, Jun 21, 2025 at 04:07:54AM +0200 schrieb Alejandro Colomar:
> I honestly still don't see the point in the camp returning NULL.  The
> only reason it hasn't died, I think, is because of fear of breaking
> existing code, but I don't see anyone asking for that behavior.
> 

I invert the question: What is the point of requesting zero bytes? If
you just want a pointer you cannot deref, but can validly plug into both
realloc() and free(), I have this perfectly good null pointer right
here. You can even statically initialize your variables with it. And you
can easily tell it apart from pointers you can dereference.

I see malloc(0) as an error. According to all standards I could get my
hands on, the argument to malloc is supposed to be the size of an
object, and in C, there are no objects without type, and no zero-sized
types. Maybe that's different in C++, I don't know. I have read a bit of
C code in my life, and have written some as well, and debugged even more
of it. I cannot recall an instance of anyone ever requesting to allocate
zero bytes except in error (e.g. uncaught overflow).

Indeed, a strict reading of the spec would be that the argument must be
the result of a sizeof expression, and the common idiom

A *a = malloc(sizeof (A) + sizeof (B));
B *b = (B *)(a + 1);

is undefined. And indeed, it potentially crashes on strict-alignment
architectures if alignof(B) > alignof(A).

Back to zero-sized allocations: The fact that they were traditionally
supported on UNIX means nothing if they were always undefined. At that
point it just becomes a quirk of the implementation, but nothing an
application should depend on.

The C89 mandate to free the pointer in case of zero-sized realloc (which
I read as a command to *only* free the pointer and not do anything else)
seems actively harmful: If an application can be tricked into doing
that, and an implementation chooses to return the now freed pointer
(since realloc must return /something/), then the application now holds
a pointer it thinks is still valid, but is actually dangling.

The way C89 (or rather FIPS160, which is the version I've read) has
added the realloc-0-frees mandate, it looks like a badly thought through
afterthought. But then, C89 also contains sprintf() and gets(), so badly
thought through afterthoughts are certainly not a novelty to that
particular writ.

All standards after it have tinkered with the semantics of zero-sized
allocation, to the point that C23 just made it explicitly undefined. As
application developer, what inference do I draw from this, except to
avoid zero-sized allocation like the plague? If anyone actually writes
"realloc(p, 0)" intending it to be the same as "free(p)", I would tell
them to then write what they mean and stop being so clever.

As implementation developer, I would therefore treat these requests as
errors, and do the appropriate thing: Return the error value and set
errno to EINVAL (and have no further side effects). And such an
implementation conforms to everything from C99 onwards.

Ciao,
Markus

Reply via email to