N2464 [0]: there was lots of implementation divergence on what realloc(ptr, 0) did (especially with BSD, which allegedly doesn't free the memory at all?), so they just declared it UB.
I read this as "there were a lot of buggy implementations of the C standard, so we imported the bug into the standard". Crazy. Don't make the language less defined going forward.
When those implementations eventually pick up C23, they surely could fix the bug as well. At best this should have been an errata/defect for the previous standard, so that the previous standards document behavior of implementations of said standards.
The BSD people don't understand what little standards they do read. It's unfortunate that we have to spoil the language for their sake.
The requirements in C99 and before are perfectly clear. realloc is described as liberating the old pointer, and then allocates a new one as if by malloc. (Except that it magically has access to both objects so it can transfer the necessary bytes that must be transferred from the old to the new.)
It is perfectly clear what happens when size is zero. No byte can be copied from the old object, if any. The behavior is like free(oldptr) followed by return malloc(newsize).
Your IQ would have to be well below 85 to misunderstand the requirements.
And those requirements are still there; there is still the description of realloc in terms of freeing the old pointer and allocating a new object with malloc.
There was no need to insert a gratuitous removal of definedness for the size zero case, given that malloc handles it.
Applications now have to do this:
void *sane_realloc(void *ptr, size_t size)
{
if (size == 0) {
// behave literally as required in C99
free(ptr);
return malloc(0);
}
return realloc(ptr, size);
}
Supposedly because a few vendors were not able to code this logic in their realloc functions?
Just a reminder to myself how happy I am to leave this academic level of snobbery behind and migrate to languages that help getting things done and save you from nonsense like this. I did C for about ten years. Hope I’ll never have to do a single line of it again.
...is actually very uncommon in the C world (at least much less common than in the C++ or Rust world).
Specific compilers and stdlibs have specific behaviours which may or may not fully agree with the details written down in the standard. You'll have to pick a subset of compilers you want to support and write your code against that subset of compilers. It's not perfect, but also not much of a problem. It's simply the reality when there are multiple compiler implementations.
In theory, it's really not much different than developing some website that has to run on mobile devices and desktops and all manners of browser implementations and versions and OS and screen sizes and be accessible to screen readers as well. Or is not JavaScript a "language that helps get things done"?
In fact, I'll take the stable, well defined compilers that I can actually test against any day.
C99 and C11 have no special treatment for a size of zero. Since "memory for the new object [of size zero] cannot be allocated, the old object is not deallocated and its value is unchanged." (emphasis added). This is exactly what BSD does.
C17 says "If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated" (emphasis added).
That all refers to a failing allocation, obviously. If realloc cannot allocate the new object, the old one stays valid.
In the case of size zero, malloc(0) can return null (always). That is ambiguous; it looks like a failure. If realloc literally were to call malloc(0) and then treats the null as a failure, it will return the old object. However, on an implementation where malloc(0) always returns null by design, that would be obviously be poor behavior for its own realloc.
If malloc(0) returns null by design that is not a case where allocating an object failed.
We basically now need this in every program that might resize to zero:
The only problem is that if malloc(0) returns null on an implementation where it normally returns an allocated pointer (and thus the call has failed) we don't detect the failure and don't preserve the original object. The application which relies on sane_realloc has to understand that when size is zero, the deallocation always works, whether or not the subsequent malloc does, and so it may get a null pointer on any platform.
Mostly, the BSD people think that POSIX and ISO C are just forks of Unix documentation, that are mainly used for making non-Unix systems look Unix-like. BSD comes from real Unix DNA and so following those is optional; whatever direction BSD takes is Unix by definition, as if it's forever 1983.
Interesting. I wonder if that means BSD's compiler toolchain has ignored the strict-aliasing misfire that makes such a mess of the ISO associated ones.
"BSD compiler toolchain" is just a GNU toolchain that is a decade plus old.
It will be probably twenty years before someone programming on BSD will have some code wrongly deleted as unreachable because it comes after realloc(ptr, 0).
They are immune from the damage.
For instance, OpenBSD 6.8 was released in 2020. On that release, gcc reports as 4.2.1, released in 2007.
When those implementations eventually pick up C23, they surely could fix the bug as well. At best this should have been an errata/defect for the previous standard, so that the previous standards document behavior of implementations of said standards.