<p>This is the first breaking change that C made, which was making <code>realloc(ptr, 0)</code> have UB instead of being roughly equivalent to <code>free(ptr)</code>.</p>
<p>C has been known to be hesitant to making 'breaking' changes such as being extra careful by adding <code>_Ugly</code> keywords that will not conflict with existing codebases.</p>
<p>So why did they see it necessary to undefine <code>realloc(ptr, 0)</code>? What problems did requiring that to have defined behavior cause?</p>

<c><undefined-behavior><memory-management>
What was the rationale for making realloc(ptr, 0) have UB in C23
CC BY-SA 4.0
2023-12-02T20:32:07.287Z
Some info here https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf
Alex K
CC BY-SA 4.0
2023-12-03T00:54:02.640Z
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.
Chris Dodd
CC BY-SA 4.0
2023-12-07T14:04:56.900Z
Also worth reading https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm
Toby Speight
CC BY-SA 4.0
2024-07-10T17:40:00.170Z
@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.
Barmar
<p>Previously, <code>realloc(ptr, 0)</code> was implementation-defined. As far as portable programming is concerned, there's little operational difference between implementation-defined behavior and undefined behavior.</p>
<p>First of all, a portable program can't depend on a specific behavior. Not all implementations would treat <code>realloc(ptr, 0)</code> like <code>free(ptr)</code>, so you could get memory leaks with some implementations.</p>
<p>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.</p>

CC BY-SA 4.0
2023-12-03T01:22:56.590Z
2023-12-03T01:22:56.590Z
Barmar
<p>And one specific reason to NOT make <code>realloc(ptr, 0)</code> like <code>free(ptr)</code> is that it would then be quite different from <code>malloc(0)</code>, which (if memory is available) will return a uniquely-addressed block.</p>
<p>I would actually argue that <code>realloc(ptr, 0)</code> should be a no-op before making it a deallocation.</p>

CC BY-SA 4.0
2024-07-18T18:08:13.250Z
2024-07-18T18:53:38.087Z
2024-07-18T18:53:38.087Z
SoronelHaetir
CC BY-SA 4.0
2024-07-22T15:45:15.767Z
Some existing code which is designed to allow selection of alternative memory allocators may treat `free(ptr)` as a wrapper for `reallocfunc(ptr,0)`.  While one could have the default value of `reallocfunc` be a pointer to a function that performs `if (size) return realloc(ptr); else { free(ptr); return 0;}`, on implementations where `realloc()` naturally behaves that way, pointing directly to `realloc()` may improve efficiency, and mandating that a C implementation supply a `realloc()` that does something different with the size-zero case would add two extra layers of logic.
supercat
<p>Suppose the Standard had specified that an implementation must predefine a constant to indicate how it handles the <code>realloc(ptr,0)</code> case, which is what &quot;implementation defined&quot; should really imply in scenarios where the number of possible behaviors is limited.  In what scenarios would such a constant actually be useful?</p>
<p>Any portable and correct code would need to accommodate two possible reasons why <code>realloc(ptr,0)</code> could return null:</p>
<ol>
<li><p>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 <code>free(ptr);</code>.</p>
</li>
<li><p>The implementation is designed to interpret <code>realloc(ptr,0)</code> as equivalent to <code>free(ptr); return 0;</code>, implying that calling code must <em>not</em> call <code>free(ptr);</code>.</p>
</li>
</ol>
<p>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 <code>realloc(ptr,0)</code> would generally exceed the amount required to write portable code that would never call that function with a <code>size</code> value of 0.</p>
<p>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.</p>

CC BY-SA 4.0
2024-07-09T21:06:39.883Z
2024-07-09T21:06:39.883Z
supercat
# What was the rationale for making realloc(ptr, 0) have UB in C23
Posted by: username_0 on 2023-12-02
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?
**Tags:** c undefined-behavior memory-management
> **Comments:**
> **username_1**: Some info here https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf  
> **username_2**: 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.  
> **username_3**: Also worth reading https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm  
> **username_4**: @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.  
---
## 3 answers
### Answer by username_4 on 2023-12-03. Score: 5
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.
---
### Answer by username_5 on 2024-07-18. Score: 1
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.
> **Comments:**
> **username_6** (2024-07-22): Some existing code which is designed to allow selection of alternative memory allocators may treat `free(ptr)` as a wrapper for `reallocfunc(ptr,0)`.  While one could have the default value of `reallocfunc` be a pointer to a function that performs `if (size) return realloc(ptr); else { free(ptr); return 0;}`, on implementations where `realloc()` naturally behaves that way, pointing directly to `realloc()` may improve efficiency, and mandating that a C implementation supply a `realloc()` that does something different with the size-zero case would add two extra layers of logic.  
---
### Answer by username_6 on 2024-07-09. Score: 0
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.
---
