16
$\begingroup$

This is the first breaking change that C made, which was making realloc(ptr, 0) have UB instead of being roughly equivalent to free(ptr).

C has been known to be hesitant to making 'breaking' changes such as being extra careful by adding _Ugly keywords that will not conflict with existing codebases.

So why did they see it necessary to undefine realloc(ptr, 0)? What problems did requiring that to have defined behavior cause?

$\endgroup$
4
  • 4
    $\begingroup$ Some info here open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf $\endgroup$
    – Alex K
    Commented Dec 2, 2023 at 20:32
  • 4
    $\begingroup$ Preivously, the behavior of realloc(ptr, 0) was implementation defined -- it was NOT "generally" equivalent to free(ptr). As to why they decided to make it undefined rather than implementation defined, it is not clear. $\endgroup$
    – Chris Dodd
    Commented Dec 3, 2023 at 0:54
  • $\begingroup$ Also worth reading open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm $\endgroup$ Commented Dec 7, 2023 at 14:04
  • $\begingroup$ @ChrisDodd Since POSIX specifies that it frees the old pointer, and many implementations are POSIX-compatible, it's likely that it "generally" does that. Note also that this change to the C standard doesn't preclude POSIX from continuing this requirement. $\endgroup$
    – Barmar
    Commented Jul 10 at 17:40

3 Answers 3

5
$\begingroup$

Previously, realloc(ptr, 0) was implementation-defined. As far as portable programming is concerned, there's little operational difference between implementation-defined behavior and undefined behavior.

First of all, a portable program can't depend on a specific behavior. Not all implementations would treat realloc(ptr, 0) like free(ptr), so you could get memory leaks with some implementations.

Second, an implementation is permitted to provide a definition of the behavior of something that the standard says is undefined. So if you're writing a program that's targeted to a particular implementation, you can take advantage of its definition of this situation.

$\endgroup$
0
$\begingroup$

Suppose the Standard had specified that an implementation must predefine a constant to indicate how it handles the realloc(ptr,0) case, which is what "implementation defined" should really imply in scenarios where the number of possible behaviors is limited. In what scenarios would such a constant actually be useful?

Any portable and correct code would need to accommodate two possible reasons why realloc(ptr,0) could return null:

  1. The implementation is for some reason unable to satisfy the request, and returns null without disturbing the original storage, implying that calling code must call free(ptr);.

  2. The implementation is designed to interpret realloc(ptr,0) as equivalent to free(ptr); return 0;, implying that calling code must not call free(ptr);.

While it would be theoretically possible for a program to test a predefined constant to determine how it should handle the null-return case, the amount of effort required to write portable code which would sometimes call realloc(ptr,0) would generally exceed the amount required to write portable code that would never call that function with a size value of 0.

Note that characterizing the action as Undefined Behavior means nothing more nor less than that the Standard imposes no requirements; it does not imply any judgment as to whether or not some treatments might be more or less useful than others in some circumstances.

$\endgroup$
0
$\begingroup$

And one specific reason to NOT make realloc(ptr, 0) like free(ptr) is that it would then be quite different from malloc(0), which (if memory is available) will return a uniquely-addressed block.

I would actually argue that realloc(ptr, 0) should be a no-op before making it a deallocation.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .