On 2024-01-18, Lawrence D'Oliveiro <[email protected]d> wrote:
On Thu, 18 Jan 2024 15:12:46 GMT, Scott Lurndal wrote:
I've never found realloc useful, regardless of the value of the size
parameter. YMMV.
Looking back through my own code, I could only find one example, from
quite a few years ago >><https://bitbucket.org/ldo17/dvd_menu_animator/src/master/>.
And screw your code ...
Kaz Kylheku <[email protected]> writes:
On 2024-01-18, Lawrence D'Oliveiro <[email protected]d> wrote:
On Thu, 18 Jan 2024 15:12:46 GMT, Scott Lurndal wrote:
I've never found realloc useful, regardless of the value
of the size parameter. YMMV.
Looking back through my own code, I could only find one example, from
quite a few years ago >>><https://bitbucket.org/ldo17/dvd_menu_animator/src/master/>.
And screw your code from years ago and its users, especially if it's
just one place in the code. At least three places are required for it
to be a issue (Microsoft Rule of Three).
I would wager dollars to donuts that Lawrence's code didn't do
a realloc(x, 0).
Kaz Kylheku <[email protected]> writes:
On 2024-01-18, James Kuyper <[email protected]> wrote:[...]
It's OK to rely upon the requirements imposed by an implementation when
the C standard doesn't impose any - but when you do so, you need to make >>> sure you actually know what those requirements are.
Exactly, and in this specific case, it's not worth the effort compared
to writing a realloc wrapper that avoids the undefined behavior, while
itself providing a C99 conforming one.
I'm not going to use realloc(ptr, 0) and check everyone's documentation.
And then what if I don't find it defined? The what? Back to the
wrapper I could have just written in the first place.
I think I would have *liked* to see C23 drop the special permission
to return a null pointer for a requested size of zero.
C11 says (and
this applies to malloc, realloc, and all other allocation functions):
If the space cannot be allocated, a null pointer is returned. 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.
This could have been changed to:
If the space cannot be allocated, a null pointer is returned. If
the size of the space requested is zero, the behavior is as
if the size were some nonzero value, except that the returned
pointer shall not be used to access an object.
Any existing implementations that always return a null pointer
for malloc(0) would have to be updated. That shouldn't be a
great burden.
Note that malloc(0) or realloc(ptr, 0) can still fail and return
a null pointer if no space can be allocated, so all allocations
should still be checked. But with this proposed change, code
could rely on realloc(ptr, 0) returning a non-null pointer *unless*
available memory is critically low -- pretty much the same as in C11,
except that a null pointer would be an indication that something
is seriously wrong.
On 2024-01-18, Blue-Maned_Hawk <[email protected]d> wrote:
Kaz Kylheku wrote:
On 2024-01-17, Blue-Maned_Hawk <[email protected]d> wrote:
It looks to me like it was always undefined behavior, but it just
wasn't explicitly stated as such in C99.
That is incorrect. A behavior for the following can be readily
inferred from C99:
void *p = malloc(42); void *q = realloc(p, 0);
Is that from an example?
No.
All the examples in the document are purely informative and cannot
define any behavior.
Of course, that's not what "behavior can be readily inferred" means, no matter where I got the example.
Kaz Kylheku wrote:
Of course, that's not what "behavior can be readily inferred" means, no
matter where I got the example.
Inferences may be makeäble, but they have no power.
But with this proposed change, code could rely on realloc(ptr,
0) returning a non-null pointer *unless* available memory is critically
low -- pretty much the same as in C11,
except that a null pointer would be an indication that something is
seriously wrong.
<https://bitbucket.org/ldo17/dvd_menu_animator/src/master/>.
I would wager dollars to donuts that Lawrence's code didn't do a
realloc(x, 0).
On Thu, 18 Jan 2024 16:13:48 -0800, Keith Thompson wrote:
But with this proposed change, code could rely on realloc(ptr,
0) returning a non-null pointer *unless* available memory is critically
low -- pretty much the same as in C11,
except that a null pointer would be an indication that something is
seriously wrong.
So having to allocate something, when it didn’t actually need to allocate anything, could lead to program failures in situations where things might otherwise work fine.
Unless, of course, there was a special non-null preallocated address value that was returned for every zero-length allocation.
Returning a null pointer for a zero-length allocation shouldn’t make any difference to the logic of your program.
Lawrence D'Oliveiro <[email protected]d> writes:
On Thu, 18 Jan 2024 16:13:48 -0800, Keith Thompson wrote:
But with this proposed change, code could rely on realloc(ptr,
0) returning a non-null pointer *unless* available memory is critically
low -- pretty much the same as in C11,
except that a null pointer would be an indication that something is
seriously wrong.
So having to allocate something, when it didn’t actually need to allocate >> anything, could lead to program failures in situations where things might
otherwise work fine.
Unless, of course, there was a special non-null preallocated address value >> that was returned for every zero-length allocation.
That wouldn't meet the current requirements. If malloc(0) returns a
non-null result, then two calls to malloc(0) must yield distinct results
(if free() isn't called in between), just as two calls to malloc(1) must
do.
to write robust code. If malloc(0) and realloc(ptr, 0) return a
non-null pointer on success, then a null result *always* indicates an allocation failure.
On Thu, 18 Jan 2024 23:47:55 GMT, Scott Lurndal wrote:
<https://bitbucket.org/ldo17/dvd_menu_animator/src/master/>.
I would wager dollars to donuts that Lawrence's code didn't do a
realloc(x, 0).
Is it cheating to look?
Tim Rentsch <[email protected]> writes:
[email protected] (Scott Lurndal) writes:
Kaz Kylheku <[email protected]> writes:
I'm looking at the C99 and N3096 (April 2023) definitions of realloc
side by side.
N3096 says
"Otherwise, if ptr does not match a pointer earlier returned by a memory >>>> management function, or if the space has been deallocated by a call to >>>> the free or realloc function, or if the size is zero, the behavior is
undefined."
Yikes! In C99 there is nothing about the size being zero:
"Otherwise, if ptr does not match a pointer earlier returned by the
calloc, malloc, or realloc function, or if the space has been
deallocated by a call to the free or realloc function, the behavior is >>>> undefined."
Nothing about "or if the size is zero".
Yikes; when did this criminal stupidity get perpetrated?
I'm not sure what stupidity you are referring to, but IIRC, there was
some recent standardization activity relating to realloc
when size == 0 because there were differences in the behavior
between different implementations. Making the behavior
undefined was the only rational choice.
Is that last sentence your own assessment, or are you simply
repeating someone else's assessment?
My assessment. I've never found realloc useful, regardless of
the value of the size parameter. YMMV.
[email protected] (Scott Lurndal) writes:
Tim Rentsch <[email protected]> writes:
[email protected] (Scott Lurndal) writes:
Kaz Kylheku <[email protected]> writes:
I'm looking at the C99 and N3096 (April 2023) definitions of realloc >>>>> side by side.
N3096 says
"Otherwise, if ptr does not match a pointer earlier returned by a memory >>>>> management function, or if the space has been deallocated by a call to >>>>> the free or realloc function, or if the size is zero, the behavior is >>>>> undefined."
Yikes! In C99 there is nothing about the size being zero:
"Otherwise, if ptr does not match a pointer earlier returned by the
calloc, malloc, or realloc function, or if the space has been
deallocated by a call to the free or realloc function, the behavior is >>>>> undefined."
Nothing about "or if the size is zero".
Yikes; when did this criminal stupidity get perpetrated?
I'm not sure what stupidity you are referring to, but IIRC, there was
some recent standardization activity relating to realloc
when size == 0 because there were differences in the behavior
between different implementations. Making the behavior
undefined was the only rational choice.
Is that last sentence your own assessment, or are you simply
repeating someone else's assessment?
My assessment. I've never found realloc useful, regardless of
the value of the size parameter. YMMV.
So you aren't really in a position to say whether this decision was
the only rational choice. Generally I hope people who would make
such a statement would first make an effort to learn and understand
other people's thoughts on the matter.
Tim Rentsch <[email protected]> writes:
[email protected] (Scott Lurndal) writes:
Kaz Kylheku <[email protected]> writes:
On 2024-01-17, Scott Lurndal <[email protected]> wrote:
Kaz Kylheku <[email protected]> writes:
Yikes; when did this criminal stupidity get perpetrated?
I'm not sure what stupidity you are referring to, but IIRC, there was >>>>> some recent standardization activity relating to realloc
when size == 0 because there were differences in the behavior
between different implementations. Making the behavior
undefined was the only rational choice.
No, the rational choice is letting those implementations be
nonconforming, until they fix their shit.
You cannot claw back decades-old defined behaviors in a major
Nobody is clawing anything back. [...]
This statement seems directly contradicted by the proposed
modification to the C standard, which changes previously
defined behavior to undefined behavior.
Clawing back implies that existing programs that rely
on either behavior will stop working.
That's not the case in the real world.
From the application portability point of view, there is little
difference between implementation defined and undefined behavior.
If realloc(ptr, 0) returns a null pointer there's no way to tell whether allocation failed (and ptr has not been freed) ...
On Sat, 20 Jan 2024 19:50:31 -0800, Keith Thompson wrote:
If realloc(ptr, 0) returns a null pointer there's no way to tell whether
allocation failed (and ptr has not been freed) ...
Actually, that doesn’t seem like a reasonable interpretation, because it leads to memory leaks.
[If] implentations could have a single dedicated object for
representing empty allocations (which can be passed to free any
number of times), that would also be a nice requirement.
What's being suggested is basically a second kind of null pointer, i.e.,
a second unique pointer value that can't be dereferenced. And if two malloc(0) calls were allowed to return the same non-null value, that
would require additional wording in the standard. I don't strongly
object to the idea, but I don't think it's necessary.
Not requiring the non-null return from malloc(0) to be distinct
from previous malloc(0) return values (whether they were freed or not),
could help to "sell" the idea of taking away the null return value.
Some implementors might grumble that null return allowed malloc(0) to be efficient by not allocating anything. If they were allowed to return
(void *) -1 or something, that would placate that concern. [...]
On 2024-01-19, Keith Thompson <[email protected]> wrote:
Kaz Kylheku <[email protected]> writes:
On 2024-01-18, James Kuyper <[email protected]> wrote:
It's OK to rely upon the requirements imposed by an implementation when >>>> the C standard doesn't impose any - but when you do so, you need to make >>>> sure you actually know what those requirements are.
Exactly, and in this specific case, it's not worth the effort compared
to writing a realloc wrapper that avoids the undefined behavior, while
itself providing a C99 conforming one.
I'm not going to use realloc(ptr, 0) and check everyone's documentation. >>>
And then what if I don't find it defined? The what? Back to the
wrapper I could have just written in the first place.
[...]
I think I would have *liked* to see C23 drop the special permission
to return a null pointer for a requested size of zero.
Yes. That would be the best thing.
It also works well when the memory is subject to memcpy or memset,
which have undefined behavior on null pointers.
On 2024-01-19, Keith Thompson <[email protected]> wrote:
Lawrence D'Oliveiro <[email protected]d> writes:
On Thu, 18 Jan 2024 16:13:48 -0800, Keith Thompson wrote:
But with this proposed change, code could rely on realloc(ptr,
0) returning a non-null pointer *unless* available memory is critically >>>> low -- pretty much the same as in C11,
except that a null pointer would be an indication that something is
seriously wrong.
So having to allocate something, when it didn’t actually need to allocate >>> anything, could lead to program failures in situations where things might >>> otherwise work fine.
Unless, of course, there was a special non-null preallocated address value >>> that was returned for every zero-length allocation.
That wouldn't meet the current requirements. If malloc(0) returns a
non-null result, then two calls to malloc(0) must yield distinct results
(if free() isn't called in between), just as two calls to malloc(1) must
do.
But if malloc(0) returns null, then two such calls don't yield distinct results.
We already don't know today whether malloc(0) == malloc(0).
to write robust code. If malloc(0) and realloc(ptr, 0) return a
non-null pointer on success, then a null result *always* indicates an
allocation failure.
Not requiring the non-null return from malloc(0) to be distinct
from previous malloc(0) return values (whether they were freed or not),
could help to "sell" the idea of taking away the null return value.
Some implementors might grumble that null return allowed malloc(0) to be efficient by not allocating anything. If they were allowed to return
(void *) -1 or something, that would placate that concern.
Say you have a large, sparse vector of dynamic vectors. Sparse in the
sense that most of the dynamic vectors in the sparse vector are empty;
only a few are nonempty. If those empty vectors come from malloc(0) in
an efficient way (nothing is allocated on the heap), that's nice.
Note that malloc(0) or realloc(ptr, 0) can still fail and return
a null pointer if no space can be allocated, so all allocations
should still be checked. [...]
I dislike the fact that the behavior is currently
implementation-defined. I think requiring malloc(0) to return a
null pointer would be an improvement over the current (C11)
specification, though it would be an odd special case; a null
pointer would mean either that the allocation failed (and the
system is likely in a bad state) or that the requested size was 0.
Note that an application might call malloc() with a variable
argument whose value can just happen to be zero. [...]
Kaz Kylheku <[email protected]> writes:
[.. considering the behavior of malloc(0) ..]
[If] implentations could have a single dedicated object for
representing empty allocations (which can be passed to free any
number of times), that would also be a nice requirement.
That defeats the whole purpose of having malloc(0) return
a non-null value. Don't you understand anything?
malloc has sort of created a rod for its own back by needing to
store the size of the allocation.
That will take up some space even when malloc(0) is called, if
NULL is not being returned. [...]
Other replies however
suggested that such malloc(0) calls need to return unique values.
But you can't have both have unique values and save memory (at
best you will need 1 byte per malloc(0), and some hairy means of
detecting whether the p in free(p) refers to one of those bytes,
so a bigger runtime overhead).
On 1/21/2024 3:55 AM, bart wrote:
malloc has sort of created a rod for its own back by needing to store[...]
the size of the allocation. That will take up some space even when
malloc(0) is called, if NULL is not being returned.
Why would malloc need to store the size of its allocations?
On 1/21/2024 3:55 AM, bart wrote:
On 19/01/2024 21:50, Kaz Kylheku wrote:[...]
On 2024-01-19, Keith Thompson <[email protected]> wrote:
Lawrence D'Oliveiro <[email protected]d> writes:
On Thu, 18 Jan 2024 16:13:48 -0800, Keith Thompson wrote:
But with this proposed change, code could rely on realloc(ptr,
0) returning a non-null pointer *unless* available memory is
critically
low -- pretty much the same as in C11,
except that a null pointer would be an indication that something is >>>>>> seriously wrong.
So having to allocate something, when it didn’t actually need to
allocate
anything, could lead to program failures in situations where things
might
otherwise work fine.
Unless, of course, there was a special non-null preallocated address >>>>> value
that was returned for every zero-length allocation.
That wouldn't meet the current requirements. If malloc(0) returns a
non-null result, then two calls to malloc(0) must yield distinct results >>>> (if free() isn't called in between), just as two calls to malloc(1) must >>>> do.
But if malloc(0) returns null, then two such calls don't yield distinct
results.
We already don't know today whether malloc(0) == malloc(0).
to write robust code. If malloc(0) and realloc(ptr, 0) return a
non-null pointer on success, then a null result *always* indicates an
allocation failure.
Not requiring the non-null return from malloc(0) to be distinct
from previous malloc(0) return values (whether they were freed or not),
could help to "sell" the idea of taking away the null return value.
Some implementors might grumble that null return allowed malloc(0) to be >>> efficient by not allocating anything. If they were allowed to return
(void *) -1 or something, that would placate that concern.
Say you have a large, sparse vector of dynamic vectors. Sparse in the
sense that most of the dynamic vectors in the sparse vector are empty;
only a few are nonempty. If those empty vectors come from malloc(0) in
an efficient way (nothing is allocated on the heap), that's nice.
malloc has sort of created a rod for its own back by needing to store
the size of the allocation. That will take up some space even when
malloc(0) is called, if NULL is not being returned.
Why would malloc need to store the size of its allocations?
On 21/01/2024 20:22, Chris M. Thomasson wrote:
On 1/21/2024 3:55 AM, bart wrote:
malloc has sort of created a rod for its own back by needing to store[...]
the size of the allocation. That will take up some space even when
malloc(0) is called, if NULL is not being returned.
Why would malloc need to store the size of its allocations?
If you do this:
p = malloc(N);
...
free(p);
'free' will need to know what N was used to allocate p in order to
deallocate right size of memory.
Personally, I think it would be often more efficient in modern C if the allocation system didn't track sizes at all, and "free" passed the
original size as a parameter. But that ship sailed long ago for
standard C.
On 21/01/2024 20:22, Chris M. Thomasson wrote:
On 1/21/2024 3:55 AM, bart wrote:
malloc has sort of created a rod for its own back by needing to store[...]
the size of the allocation. That will take up some space even when
malloc(0) is called, if NULL is not being returned.
Why would malloc need to store the size of its allocations?
If you do this:
p = malloc(N);
...
free(p);
'free' will need to know what N was used to allocate p in order to
deallocate right size of memory.
David Brown <[email protected]> writes:
[...]
Personally, I think it would be often more efficient in modern C if
the allocation system didn't track sizes at all, and "free" passed the
original size as a parameter. But that ship sailed long ago for
standard C. (C++ supports a sized deallocation system, and of course
there's nothing to stop you making your own allocator system for C.)
I suspect that calling malloc() with one size and free() with a
different one would have been a rich source of subtle bugs.
On 21/01/2024 21:34, bart wrote:
On 21/01/2024 20:22, Chris M. Thomasson wrote:
On 1/21/2024 3:55 AM, bart wrote:
malloc has sort of created a rod for its own back by needing to[...]
store the size of the allocation. That will take up some space even
when malloc(0) is called, if NULL is not being returned.
Why would malloc need to store the size of its allocations?
If you do this:
p = malloc(N);
...
free(p);
'free' will need to know what N was used to allocate p in order to
deallocate right size of memory.
It does not need to store the size of the allocation.
It merely has to
store sufficient information to be able to figure out how to deallocate properly. And any storage it needs can be in a different place from the allocation.
Some malloc/free systems track the information separately from the
allocation data. One possibility is to use pools for different memory sizes. When the user asks for X bytes, this is rounded up to the
nearest pool size - call it Y - and the allocation is made from the Y
pool, which can be viewed as an array of Y-size lumps. Only need one
single bit to track each allocation, to say which indexes in the array
are used. There are many other ways to do it.
Personally, I think it would be often more efficient in modern C if the allocation system didn't track sizes at all, and "free" passed the
original size as a parameter.
On 2024-01-21, Keith Thompson <[email protected]> wrote:
David Brown <[email protected]> writes:
[...]
Personally, I think it would be often more efficient in modern C if
the allocation system didn't track sizes at all, and "free" passed the
original size as a parameter. But that ship sailed long ago for
standard C. (C++ supports a sized deallocation system, and of course
there's nothing to stop you making your own allocator system for C.)
I suspect that calling malloc() with one size and free() with a
different one would have been a rich source of subtle bugs.
Welcome to C23! free_sized(malloc(42), 73) -> UB.
On 2024-01-21, Lawrence D'Oliveiro <[email protected]d> wrote:
On Sat, 20 Jan 2024 19:50:31 -0800, Keith Thompson wrote:
If realloc(ptr, 0) returns a null pointer there's no way to tell whether >>> allocation failed (and ptr has not been freed) ...
Actually, that doesn?t seem like a reasonable interpretation, because it
leads to memory leaks.
That's not an "interpretation"; that's just a fact.
A null return from realloc has two documented meanings; situations exist
in which it cannot be distinguished which one, in any portable way.
Keith Thompson <[email protected]> writes:
Kaz Kylheku <[email protected]> writes:
On 2024-01-18, James Kuyper <[email protected]> wrote:
It's OK to rely upon the requirements imposed by an implementation when >>>> the C standard doesn't impose any - but when you do so, you need to make >>>> sure you actually know what those requirements are.
Exactly, and in this specific case, it's not worth the effort compared
to writing a realloc wrapper that avoids the undefined behavior, while
itself providing a C99 conforming one.
I'm not going to use realloc(ptr, 0) and check everyone's documentation. >>>
And then what if I don't find it defined? The what? Back to the
wrapper I could have just written in the first place.
[...]
I think I would have *liked* to see C23 drop the special permission
to return a null pointer for a requested size of zero. C11 says (and
this applies to malloc, realloc, and all other allocation functions):
If the space cannot be allocated, a null pointer is returned. 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.
This could have been changed to:
If the space cannot be allocated, a null pointer is returned. If
the size of the space requested is zero, the behavior is as
if the size were some nonzero value, except that the returned
pointer shall not be used to access an object.
Any existing implementations that always return a null pointer
for malloc(0) would have to be updated. That shouldn't be a
great burden.
Note that malloc(0) or realloc(ptr, 0) can still fail and return
a null pointer if no space can be allocated, so all allocations
should still be checked. But with this proposed change, code
could rely on realloc(ptr, 0) returning a non-null pointer *unless*
available memory is critically low -- pretty much the same as in C11,
except that a null pointer would be an indication that something
is seriously wrong.
(I remember seeing a discussion about making the behavior of
realloc(ptr, 0) undefined. I'm making inquiries, and I'll follow
up if I learn anything relevant.)
I got a response from JeanHeyd Meneide.
If realloc(ptr, 0) returns a null pointer there's no way to tell whether allocation failed (and ptr has not been freed), or the implementation
returns a null pointer for zero-sized allocations (and ptr has been
freed). Some implementations set errno, but C doesn't require it.
Tim Rentsch <[email protected]> writes:
It's trivial to fix that problem: simply require implementations
to define a preprocessor symbol about how the implementation
works. Problem solved.
(There are other instances of implementation-defined behavior
that would benefit from analogous changes along these lines.)
I tend to agree that such a preprocessor symbol would be an improvement.
I still think, as I wrote above, that removing the permission to
return a null pointer on a successful zero-sized allocation would be a greater improvement.
A preprocessor symbol makes it easier for programmers to work around the potential difference between implementations. The change I advocate
would make it completely unnecessary.
Except, of course, that most code would still have to allow for pre-C26 behavior, even if the change were adopted in C26. That's unavoidable in
the absence of time machines. On the gripping hand, since it seems that
most existing implementations (well, all of the few I've tried) return a non-null pointer for malloc(0), it might be reasonable to ignore the few pre-C26 implementations that return a null pointer.
David Brown <[email protected]> writes:
[...]
Personally, I think it would be often more efficient in modern C if
the allocation system didn't track sizes at all, and "free" passed the
original size as a parameter. But that ship sailed long ago for
standard C. (C++ supports a sized deallocation system, and of course
there's nothing to stop you making your own allocator system for C.)
I suspect that calling malloc() with one size and free() with a
different one would have been a rich source of subtle bugs.
On 2024-01-21, David Brown <[email protected]> wrote:
Personally, I think it would be often more efficient in modern C if the
allocation system didn't track sizes at all, and "free" passed the
original size as a parameter. But that ship sailed long ago for
standard C.
That ship newly sailed in 2023.
The N3096 draft describes a function free_sized that takes a size. If
the size is wrong, the behavior is undefined.
So now C has two ways to free an object: an efficient one where
the program helps by giving the size, and the old free, which
may have to do more work.
I think going forward, it may start to become wise to detect the implementation's support for free_sized (e.g. in a configure script)
and use that as much as possible, using free only when it's
inconvenient: for instance a function resembling POSIX strdup
might not be able to to safely assume that it can do
free_sized(str, strlen(str)+1), because the underlying buffer
might be larger.
That's a half-assed argument. There are other ways a pointer might
have a null value than just being the result of a call to malloc().
If code might call memset() et al with a zero size and a null
pointer, it's better to address all possible cases at once rather
than just some of them:
static inline void *
safer_memset( void *s, int c, size_t n ){
return n ? memset( s, c, n ) : s;
}
static inline void *
safer_memcpy( void *d, const void *s, size_t n ){
return n ? memcpy( d, s, n ) : d;
}
/* ... etc ... */
malloc has sort of created a rod for its own back by needing to store
the size of the allocation. That will take up some space even when
malloc(0) is called, if NULL is not being returned.
On 1/21/2024 12:34 PM, bart wrote:
On 21/01/2024 20:22, Chris M. Thomasson wrote:
On 1/21/2024 3:55 AM, bart wrote:
malloc has sort of created a rod for its own back by needing to[...]
store the size of the allocation. That will take up some space even
when malloc(0) is called, if NULL is not being returned.
Why would malloc need to store the size of its allocations?
If you do this:
p = malloc(N);
...
free(p);
'free' will need to know what N was used to allocate p in order to
deallocate right size of memory.
Why?
BTW if I run this program (in my language which uses an allocator that requires a size to free):
const n=1024
ref byte p:=pcm_alloc(n), lastp
I get these results:...
02E2D440 1024
02E2D840 1024
bart <[email protected]> writes:
malloc has sort of created a rod for its own back by needing to store
the size of the allocation. That will take up some space even when
malloc(0) is called, if NULL is not being returned.
The alternative is making a rod for the back of every single caller
(i.e. all consumers must track allocation sizes).
I think the design of malloc got this one right.
On 21/01/2024 12:04, Tim Rentsch wrote:
Kaz Kylheku <[email protected]> writes:No, because it's natural to write
[...]
Not requiring the non-null return from malloc(0) to be distinct
from previous malloc(0) return values (whether they were freed or not),
could help to "sell" the idea of taking away the null return value.
Some implementors might grumble that null return allowed malloc(0) to be >>> efficient by not allocating anything. If they were allowed to return
(void *) -1 or something, that would placate that concern. [...]
You have the tail wagging the dog here. If the results of
different malloc(0) calls don't need to be distinguishable,
they might just as well all be null.
employees = malloc(Nemployees * sizeof(EMPLOYEE));
if (!employees)
goto out_of_memory;
You don't want to have to special case Nemployees == 0.
Richard Kettlewell <[email protected]d> writes:
Fully agreed. That permission has been grit in the gears for a very long
time, for much of which I had the misfortune of having to deal with it
in real life thanks to IBM’s bad decisions.
Can you expand on "IBM's bad decisions"?
Standard C is trying to have its own cake and eat it here: 0-sized allocations can be represented by null pointers when it’s malloc, but
not when it’s memcpy.
Keith Thompson <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
Fully agreed. That permission has been grit in the gears for a very long >>> time, for much of which I had the misfortune of having to deal with it
in real life thanks to IBM’s bad decisions.
Can you expand on "IBM's bad decisions"?
AIX is one of the platforms that returns NULL for malloc(0) even when >there’s some memory available (in fact the only one I know).
Richard Kettlewell <[email protected]d> writes:
AIX is one of the platforms that returns NULL for malloc(0) even when >>there’s some memory available (in fact the only one I know).
AIX had this odd 'overcommit' feature, which allowed the system to
overcommit memory resources. It would allocate virtual address space
(via malloc) even if there wasn't enough memory + backing store to
support it, and would take a SIGSEGV when referenced if, at the time
of reference, there was still insufficient memory and/or backing store
to support allocating a physical page to that portion of the VA space.
Linux can be configured to operate in the same way.
I don't believe AIX ever returned NULL from malloc() while still
allocating VA space for the allocation.
On 22/01/2024 10:22, Richard Kettlewell wrote:
I think the design of malloc got this one right.
I think it got it wrong. Now everyone is lumbered with an allocation
scheme that will ALWAYS have to store the size of that struct, perhaps
taking as much space as the struct itself.
Imagine allocating 100M of those structs, and also storing 100M copies
of sizeof(Date) or whatever metadata is needed.
Getting around that, by writing your own small-object allocator on top
of malloc, is a lot harder that adding your own size-tracking on top of
a malloc that does not store any extra data. As I've shown.
[...] Every such allocation results in a pointer. The
program has to retain at least one copy of that pointer.
The pointer is probably also 8 bits wide, so [...]
On 21/01/2024 22:31, Keith Thompson wrote:
I suspect that calling malloc() with one size and free() with aSure. But that's part of the fun of C :-)
different one would have been a rich source of subtle bugs.
Then on Windows I might get:
...
Some odd results ...
On 2024-01-22, bart <[email protected]> wrote:
On 22/01/2024 10:22, Richard Kettlewell wrote:
I think the design of malloc got this one right.
I think it got it wrong. Now everyone is lumbered with an allocation
scheme that will ALWAYS have to store the size of that struct, perhaps
taking as much space as the struct itself.
Implementations of malloc have special strategies for small objects,
to allocate them compactly, with minimal overhead and fragmentation.
It is not necessary to store the size in every such object.
Imagine allocating 100M of those structs, and also storing 100M copies
of sizeof(Date) or whatever metadata is needed.
For the size to take up as much object as the struct itself, it
has to be a struct that whose size is sizeof(size_t), which is tiny:
often 8 bytes nowadays, or possibly 4.
An object that is as small as size_t should just be passed around
by value. It likely fits into a machine register, and so dynamically allocating it is inefficient.
Every such allocation results in a pointer. The program has to retain at least one copy of that pointer. The pointer is probably also 8 bits
wide, so even if those objects are allocated compactly without storing a
size field, it takes double the space just to retain pointers to them.
Getting around that, by writing your own small-object allocator on top
of malloc, is a lot harder that adding your own size-tracking on top of
a malloc that does not store any extra data. As I've shown.
Writing small-object allocators on malloc is mostly a fool's errand,
in the absence of special justification.
On 2024-01-22, bart <[email protected]> wrote:
On 22/01/2024 10:22, Richard Kettlewell wrote:
I think the design of malloc got this one right.
I think it got it wrong. Now everyone is lumbered with an allocation
scheme that will ALWAYS have to store the size of that struct, perhaps
taking as much space as the struct itself.
Implementations of malloc have special strategies for small objects,
to allocate them compactly, with minimal overhead and fragmentation.
It is not necessary to store the size in every such object.
Imagine allocating 100M of those structs, and also storing 100M copies
of sizeof(Date) or whatever metadata is needed.
For the size to take up as much object as the struct itself, it
has to be a struct that whose size is sizeof(size_t), which is tiny:
often 8 bytes nowadays, or possibly 4.
An object that is as small as size_t should just be passed around
by value. It likely fits into a machine register, and so dynamically >allocating it is inefficient.
Imagine allocating 100M of those structs, and also storing 100M copies
of sizeof(Date) or whatever metadata is needed.
On 2024-01-19, Blue-Maned_Hawk <[email protected]d> wrote:
Kaz Kylheku wrote:
Of course, that's not what "behavior can be readily inferred" means,
no matter where I got the example.
Inferences may be makeäble, but they have no power.
Yeah, tell that to your compiler when it surprisingly deletes code based
on inference.
Tim Rentsch <[email protected]> writes:
That's a half-assed argument. There are other ways a pointer might
have a null value than just being the result of a call to malloc().
If code might call memset() et al with a zero size and a null
pointer, it's better to address all possible cases at once rather
than just some of them:
static inline void *
safer_memset( void *s, int c, size_t n ){
return n ? memset( s, c, n ) : s;
}
static inline void *
safer_memcpy( void *d, const void *s, size_t n ){
return n ? memcpy( d, s, n ) : d;
}
/* ... etc ... */
Of course that's what the cautious programmer must do practice. But in
terms of the total cost (over all users, implementers, etc) fixing the definitions of memcpy/memset/etc (as well as malloc) would have been the better answer.
Standard C is trying to have its own cake and eat it here: 0-sized allocations can be represented by null pointers when it's malloc, but
not when it's memcpy.
Keith Thompson <[email protected]> writes:
Tim Rentsch <[email protected]> writes:
It's trivial to fix that problem: simply require implementations
to define a preprocessor symbol about how the implementation
works. Problem solved.
(There are other instances of implementation-defined behavior
that would benefit from analogous changes along these lines.)
I tend to agree that such a preprocessor symbol would be an
improvement.
I still think, as I wrote above, that removing the permission to
return a null pointer on a successful zero-sized allocation would
be a greater improvement.
Fully agreed. That permission has been grit in the gears for a
very long time, for much of which I had the misfortune of having
to deal with it in real life thanks to IBM's bad decisions.
Fixing it would have been, what, a 2-line change to the impacted implementations, [...]
On 21/01/2024 12:04, Tim Rentsch wrote:
Kaz Kylheku <[email protected]> writes:
[...]
Not requiring the non-null return from malloc(0) to be distinct
from previous malloc(0) return values (whether they were freed or not),
could help to "sell" the idea of taking away the null return value.
Some implementors might grumble that null return allowed malloc(0) to be >>> efficient by not allocating anything. If they were allowed to return
(void *) -1 or something, that would placate that concern. [...]
You have the tail wagging the dog here. If the results of
different malloc(0) calls don't need to be distinguishable,
they might just as well all be null.
No, because it's natural to write
employees = malloc(Nemployees * sizeof(EMPLOYEE));
if (!employees)
goto out_of_memory;
You don't want to have to special case Nemployees == 0.
Kaz Kylheku wrote:
On 2024-01-19, Blue-Maned_Hawk <[email protected]d> wrote:
Kaz Kylheku wrote:
Of course, that's not what "behavior can be readily inferred" means,
no matter where I got the example.
Inferences may be makeäble, but they have no power.
Yeah, tell that to your compiler when it surprisingly deletes code based
on inference.
Apologies, let me clarify: In a technical standard, inferences from nonnormative text cannot be used to make normative statements.
On 2024-01-17, Blue-Maned_Hawk <[email protected]d> wrote:
It looks to me like it was always undefined behavior, but it just wasn't
explicitly stated as such in C99.
That is incorrect. A behavior for the following can be readily inferred
from C99:
void *p = malloc(42);
void *q = realloc(p, 0);
On Mon, 22 Jan 2024 09:24:26 +0100, David Brown wrote:
On 21/01/2024 22:31, Keith Thompson wrote:
I suspect that calling malloc() with one size and free() with aSure. But that's part of the fun of C :-)
different one would have been a rich source of subtle bugs.
Not so much fun for the poor users who paid you the big bucks and expected
to get working code in return.
Richard Kettlewell <[email protected]d> writes:
Of course that's what the cautious programmer must do practice. But in
terms of the total cost (over all users, implementers, etc) fixing the
definitions of memcpy/memset/etc (as well as malloc) would have been the
better answer.
Better in some ways, worse in others. Better for me is not
always the same as better for thee.
Standard C is trying to have its own cake and eat it here: 0-sized
allocations can be represented by null pointers when it's malloc, but
not when it's memcpy.
Actually the two decisions have essentially nothing to do with
each other. You might want to read what the C Rationale
document has to say about the decisions behind various
memory allocation policies.
Tim Rentsch <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
Of course that's what the cautious programmer must do practice. But in
terms of the total cost (over all users, implementers, etc) fixing the
definitions of memcpy/memset/etc (as well as malloc) would have been the >>> better answer.
Better in some ways, worse in others. Better for me is not
always the same as better for thee.
The way I think it's better is total cost over all users, implementers,
etc. [...]
Standard C is trying to have its own cake and eat it here: 0-sized
allocations can be represented by null pointers when it's malloc, but
not when it's memcpy.
Actually the two decisions have essentially nothing to do with
each other. You might want to read what the C Rationale
document has to say about the decisions behind various
memory allocation policies.
Do you mean
https://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
s7.20.3?
Tim Rentsch <[email protected]> writes:
[email protected] (Scott Lurndal) writes:
Tim Rentsch <[email protected]> writes:
[email protected] (Scott Lurndal) writes:
Kaz Kylheku <[email protected]> writes:
I'm looking at the C99 and N3096 (April 2023) definitions of realloc >>>>>> side by side.
N3096 says
"Otherwise, if ptr does not match a pointer earlier returned by a memory >>>>>> management function, or if the space has been deallocated by a call to >>>>>> the free or realloc function, or if the size is zero, the behavior is >>>>>> undefined."
Yikes! In C99 there is nothing about the size being zero:
"Otherwise, if ptr does not match a pointer earlier returned by the >>>>>> calloc, malloc, or realloc function, or if the space has been
deallocated by a call to the free or realloc function, the behavior is >>>>>> undefined."
Nothing about "or if the size is zero".
Yikes; when did this criminal stupidity get perpetrated?
I'm not sure what stupidity you are referring to, but IIRC, there was >>>>> some recent standardization activity relating to realloc
when size == 0 because there were differences in the behavior
between different implementations. Making the behavior
undefined was the only rational choice.
Is that last sentence your own assessment, or are you simply
repeating someone else's assessment?
My assessment. I've never found realloc useful, regardless of
the value of the size parameter. YMMV.
So you aren't really in a position to say whether this decision was
the only rational choice. Generally I hope people who would make
such a statement would first make an effort to learn and understand
other people's thoughts on the matter.
The fact that I didn't find it useful, doesn't imply in any way
that I don't understand other's thoughts on the matter. I did
spend several years on various standards committees, compromise
was often the best way to make forward progress and 'undefined
behavior' was a rational compromise in that context.
Richard Kettlewell <[email protected]d> writes:
Tim Rentsch <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
Of course that's what the cautious programmer must do practice. But in >>>> terms of the total cost (over all users, implementers, etc) fixing the >>>> definitions of memcpy/memset/etc (as well as malloc) would have been the >>>> better answer.
Better in some ways, worse in others. Better for me is not
always the same as better for thee.
The way I think it's better is total cost over all users, implementers,
etc. [...]
Yes, I got that. Not everyone shares that view. It's not
even a given that everyone thinks that's the best metric
to optimize.
AIX had this odd 'overcommit' feature, which allowed the system to
overcommit memory resources. It would allocate virtual address space
(via malloc) even if there wasn't enough memory + backing store to
support it, and would take a SIGSEGV when referenced if, at the time of reference, there was still insufficient memory and/or backing store to support allocating a physical page to that portion of the VA space.
Linux can be configured to operate in the same way.
Tim Rentsch <[email protected]> writes:
Keith Thompson <[email protected]> writes:
Keith Thompson <[email protected]> writes:
I think I would have *liked* to see C23 drop the special permission
to return a null pointer for a requested size of zero. C11 says (and
this applies to malloc, realloc, and all other allocation functions):
[...]
Note that malloc(0) or realloc(ptr, 0) can still fail and return
a null pointer if no space can be allocated, so all allocations
should still be checked. But with this proposed change, code
could rely on realloc(ptr, 0) returning a non-null pointer *unless*
available memory is critically low -- pretty much the same as in C11,
except that a null pointer would be an indication that something
is seriously wrong.
(I remember seeing a discussion about making the behavior of
realloc(ptr, 0) undefined. I'm making inquiries, and I'll follow
up if I learn anything relevant.)
I got a response from JeanHeyd Meneide.
If realloc(ptr, 0) returns a null pointer there's no way to tell
whether allocation failed (and ptr has not been freed), or the
implementation returns a null pointer for zero-sized allocations
(and ptr has been freed). Some implementations set errno, but C
doesn't require it.
It's trivial to fix that problem: simply require implementations
to define a preprocessor symbol about how the implementation
works. Problem solved.
(There are other instances of implementation-defined behavior
that would benefit from analogous changes along these lines.)
I tend to agree that such a preprocessor symbol would be an
improvement.
I still think, as I wrote above, that removing the permission to
return a null pointer on a successful zero-sized allocation would
be a greater improvement.
A preprocessor symbol makes it easier for programmers to work
around the potential difference between implementations. The
change I advocate would make it completely unnecessary.
Except, of course, that most code would still have to allow for
pre-C26 behavior, even if the change were adopted in C26. That's
unavoidable in the absence of time machines. On the gripping
hand, since it seems that most existing implementations (well, all
of the few I've tried) return a non-null pointer for malloc(0), it
might be reasonable to ignore the few pre-C26 implementations that
return a null pointer.
I see no problem in special casing [zero].
On 1/22/24 18:12, Blue-Maned_Hawk wrote:
Kaz Kylheku wrote:
On 2024-01-19, Blue-Maned_Hawk <[email protected]d> wrote:
Kaz Kylheku wrote:
Of course, that's not what "behavior can be readily inferred"
means, no matter where I got the example.
Inferences may be makeable, but they have no power.
Yeah, tell that to your compiler when it surprisingly deletes
code based on inference.
Apologies, let me clarify: In a technical standard, inferences
from nonnormative text cannot be used to make normative statements.
True, but the relevant inferences were from normative text. The
context of the above comments has been snipped, so I'll repeat it
here:
On 1/17/24 19:10, Kaz Kylheku wrote:
On 2024-01-17, Blue-Maned_Hawk <[email protected]d> wrote:
It looks to me like it was always undefined behavior, but it
just wasn't explicitly stated as such in C99.
That is incorrect. A behavior for the following can be readily
inferred from C99:
void *p = malloc(42);
Per C99:
"The malloc function returns either a null pointer or a pointer to
the allocated space." (7.22.3.4p2)
Thus, there are two possibilities:
A: p == NULL
B: p contains the address of a block of memory at least 42 bytes
long, suitably aligned to store an object of any type.
The standard does not mandate documentation of which of the two
applies, so this has unspecified behavior.
... however, these calls
void *xx = realloc( x, 0 );
void *yy = realloc( y, 0 );
both return a null pointer, and subsequent code shows that both x and y
have indeed been deallocated. (Both gcc and clang give the same result, which isn't surprising since both are using glibc.)
You could have just read the docs
<https://manpages.debian.org/3/realloc.en.html>.
I have an observation worth mentioning. Using glibc (tested in
Ubuntu), these assignments
void *x = malloc( 0 );
void *y = malloc( 1 );
both return a non-null pointer. Following that, however, these
calls
void *xx = realloc( x, 0 );
void *yy = realloc( y, 0 );
both return a null pointer, and subsequent code shows that both x
and y have indeed been deallocated. (Both gcc and clang give the
same result, which isn't surprising since both are using glibc.)
Keith Thompson <[email protected]> writes:
Tim Rentsch <[email protected]> writes:
Keith Thompson <[email protected]> writes:
I got a response from JeanHeyd Meneide.
If realloc(ptr, 0) returns a null pointer there's no way to tell
whether allocation failed (and ptr has not been freed), or the
implementation returns a null pointer for zero-sized allocations
(and ptr has been freed). Some implementations set errno, but C
doesn't require it.
(Let me mention in passing that there is a way to tell that
should work in practice on any non-malicious implementation.)
On Mon, 22 Jan 2024 18:43:06 GMT, Scott Lurndal wrote:
AIX had this odd 'overcommit' feature, which allowed the system to
overcommit memory resources. It would allocate virtual address space
(via malloc) even if there wasn't enough memory + backing store to
support it, and would take a SIGSEGV when referenced if, at the time of
reference, there was still insufficient memory and/or backing store to
support allocating a physical page to that portion of the VA space.
Linux can be configured to operate in the same way.
Not quite the same. Linux overcommit means “never having to say no”. If >the system runs short of memory, then this wakes up the Dreaded OOM
Killer, which starts scoring processes on their hunger for memory, and >killing the ones that are the biggest memory hogs.
On 24/01/2024 04:28, Tim Rentsch wrote:...
...(Let me mention in passing that there is a way to tell that
should work in practice on any non-malicious implementation.)
Please either explain yourself, or stop making such claims.
(Note to others - Tim either does not read my posts, or actively
refuses to reply to them. If anyone else wants to know his method,
they will need to reply themselves.)
Richard Kettlewell <[email protected]d> writes:
Tim Rentsch <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
Of course that's what the cautious programmer must do practice. But in >>>> terms of the total cost (over all users, implementers, etc) fixing the >>>> definitions of memcpy/memset/etc (as well as malloc) would have been the >>>> better answer.
Better in some ways, worse in others. Better for me is not
always the same as better for thee.
The way I think it's better is total cost over all users, implementers,
etc. [...]
Yes, I got that. Not everyone shares that view.
It's not even a given that everyone thinks that's the best metric to optimize.
Standard C is trying to have its own cake and eat it here: 0-sized
allocations can be represented by null pointers when it's malloc, but
not when it's memcpy.
Actually the two decisions have essentially nothing to do with
each other. You might want to read what the C Rationale
document has to say about the decisions behind various
memory allocation policies.
Do you mean
https://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
s7.20.3?
I was looking at C99RationaleV5.10, although I believe the comments
are from the earlier, original Rationale document. I expect you
have the right section; I don't have the document open to check.
Actually the two decisions have essentially nothing to do with
each other.
On 2024-01-21, Tim Rentsch <[email protected]> wrote:
Kaz Kylheku <[email protected]> writes:
[.. considering the behavior of malloc(0) ..]
[If] implentations could have a single dedicated object for
representing empty allocations (which can be passed to free any
number of times), that would also be a nice requirement.
That defeats the whole purpose of having malloc(0) return
a non-null value. Don't you understand anything?
I undertand very clearly from past disccussions that you're attached
to a particular use case whereby malloc(0) returns unique objects.
However, I don't see that as the purpose, let alone the "whole
purpose".
The standard currently does not endorse malloc(0) as a factory
for unique pointers.
Currently, it cannot be relied on due to the possibility of the null
return. If you want that behavior portably, you currently have to
use malloc(1) instead, or some other nonzero value. Even if your
program detects that malloc(0) returns a non-null pointer one time,
there is no requirement that all subsequent such allocations will
return non-null.
James Kuyper <[email protected]> writes:...
On 1/22/24 18:12, Blue-Maned_Hawk wrote:
void *p = malloc(42);
Per C99:
"The malloc function returns either a null pointer or a pointer to
the allocated space." (7.22.3.4p2)
Thus, there are two possibilities:
A: p == NULL
B: p contains the address of a block of memory at least 42 bytes
long, suitably aligned to store an object of any type.
The standard does not mandate documentation of which of the two
applies, so this has unspecified behavior.
The return value from malloc() is not unspecified behavior. In
n1256, see section 7.20.3 paragraph 1, and also the definition of
unspecified behavior in 3.4.4.
Your understanding of the term "implementation-defined" is flawed.
"Chris M. Thomasson" <[email protected]> writes:
On 1/25/2024 11:35 AM, Keith Thompson wrote:
[...]
Unfortunately, code does not have access to the implementation's
documentation.
Mind if I quote that from time to time? Nice one.
Feel free.
To expand on that, I think it was Tim Rentsch who recently suggested
adding predefined macros that code can query to find out which choices
an implementation has made. I'm concerned that that would introduce a
lot of new symbols into the global namespace, so I'm thinking about a
new preprocessor pseudo-function, something like:
#if IMPL(MALLOC0, NON_NULL_RESULT)
...
#elif IMPL(MALLOC0, NULL_RESULT)
...
#endif
This is just off the top of my head. On the other hand, a bunch of
macros that can be queried with #ifdef would be simpler to implement.
On 2024-01-23, Tim Rentsch <[email protected]> wrote:
Richard Kettlewell <[email protected]d> writes:
Tim Rentsch <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
Of course that's what the cautious programmer must do practice. But in >>>>> terms of the total cost (over all users, implementers, etc) fixing the >>>>> definitions of memcpy/memset/etc (as well as malloc) would have been the >>>>> better answer.
Better in some ways, worse in others. Better for me is not
always the same as better for thee.
The way I think it's better is total cost over all users, implementers,
etc. [...]
Yes, I got that. Not everyone shares that view. It's not
even a given that everyone thinks that's the best metric
to optimize.
It is an inherently collectivist view; I would expect communists
to agree with it unanimously. (But then bungle the execution by
forming a five year plan in which the work is divided into groups
driven by meaningless local metrics and irrelevant incentives.)
Tim Rentsch <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
Tim Rentsch <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
Of course that's what the cautious programmer must do practice.
But in terms of the total cost (over all users, implementers,
etc) fixing the definitions of memcpy/memset/etc (as well as
malloc) would have been the better answer.
Better in some ways, worse in others. Better for me is not
always the same as better for thee.
The way I think it's better is total cost over all users,
implementers, etc. [...]
Yes, I got that. Not everyone shares that view.
The cost is an objective statement, not a view.
In principle I could be wrong that the cost of everyone dealing
with the resulting bugs, adding workarounds, etc, is greater than
the cost of fixing implementations, but frankly it doesn't seem
very plausible.
It's not even a given that everyone thinks that's the best metric
to optimize.
C's history since standardization does include a disregard for the
costs born by its users, yes.
Standard C is trying to have its own cake and eat it here:
0-sized allocations can be represented by null pointers when
it's malloc, but not when it's memcpy.
Actually the two decisions have essentially nothing to do with
each other. You might want to read what the C Rationale
document has to say about the decisions behind various
memory allocation policies.
Do you mean
https://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
s7.20.3?
I was looking at C99RationaleV5.10, although I believe the comments
are from the earlier, original Rationale document. I expect you
have the right section; I don't have the document open to check.
It is thoroughly unenlightening as an explanation.
* It says the rules about zero-length allocations are intended to
permit a particular paradigm, of which an example is given.
* The example includes no attempt at error handling, which is
where the pain point from the permission for malloc(0)=NULL
arises. So it seems like the author of that section didn't
consider the impact of the rules adopted.
* Nothing about the example depends either on malloc(0)=NULL or
malloc(0)!=NULL, making it mysterious why the example is said
to support the decisions made in C89.
* The text talks about the distinction betwen 'nothing' and 'zero'
but in the context of malloc the distinction the caller actually
needs to make is between success and failure.
* The principle that 'zero-length objects' are invalid does not
seem to be rationalized anywhere in the text. While there may
be some advantage to it, nobody wrote them down.
A couple of other notes:
* The Unix V7 rules for malloc neatly bypass any concerns about
zero-length objects (if anybody cared about that at the time);
the definition is "returns a pointer to a block of at least size
bytes". malloc(0) returning a pointer to a 1-byte block would be
a non-issue under this definition regardless of any theoretical
objections.
* If it happens that c=0 at any point in the loop, the new C23
rules make the example in 7.20.3 undefined behavior. I guess
the example has fallen out of favour.
Finally back to the relationship to malloc:
Actually the two decisions have essentially nothing to do with
each other.
It does seem like nobody considered the question of whether the
two decisions were compatible; [...]
On 2024-01-25, Tim Rentsch <[email protected]> wrote:
Your understanding of the term "implementation-defined" is flawed.
No it isn't.
Obviously, an implementation cannot vaccilate among different
choices of how malloc(0) should behave. [...]
I apologize for not making all this reasoning clearer in my
posting.
Richard Kettlewell <[email protected]d> writes:
C's history since standardization does include a disregard for the
costs born by its users, yes.
I don't think that's true. Probably it is true that the
priorities of folks who have worked on the ISO C committee
don't exactly match your priorities.
It is thoroughly unenlightening as an explanation.
Oh. I suppose that depends on what it is one thinks the writing
is trying to explain.
* The example includes no attempt at error handling, which is
where the pain point from the permission for malloc(0)=NULL
arises. So it seems like the author of that section didn't
consider the impact of the rules adopted.
The code pattern shown is something that was observed to be in
widespread use, not something that the Rationale's authors
constructed to illustrate a point. It isn't surprising that
there isn't any mention of error handling, because it isn't
important to what the section is trying to explain.
* Nothing about the example depends either on malloc(0)=NULL or
malloc(0)!=NULL, making it mysterious why the example is said
to support the decisions made in C89.
Nothing in the Rationale document says that the given code
pattern supports the decisions made in the C standard. Any
implication actually goes the other direction, that decisions
about how malloc() works were guided (in part) by a desire to
support the given code pattern.
* The principle that 'zero-length objects' are invalid does not
seem to be rationalized anywhere in the text. While there may
be some advantage to it, nobody wrote them down.
What the text actually says is that the C89 Committee decided not
to accept the idea of zero-length objects. Probably the phrasing
could have been better; in any case what is meant is that they
decided not to /require/ support of zero-length objects, but
rather have it be optional (and implementation-defined).
A couple of other notes:
* The Unix V7 rules for malloc neatly bypass any concerns about
zero-length objects (if anybody cared about that at the time);
the definition is "returns a pointer to a block of at least size
bytes". malloc(0) returning a pointer to a 1-byte block would be
a non-issue under this definition regardless of any theoretical
objections.
I don't know anything about what Unix V7 says about malloc(), so
I have nothing to say about that.
* If it happens that c=0 at any point in the loop, the new C23
rules make the example in 7.20.3 undefined behavior. I guess
the example has fallen out of favour.
Invalidating working code is a significant part of why people
object to the decision to make zero-sized realloc() calls be
undefined behavior.
Finally back to the relationship to malloc:
Actually the two decisions have essentially nothing to do with
each other.
It does seem like nobody considered the question of whether the
two decisions were compatible; [...]
They may or may not have. You may want to read the Introduction
section of the Rationale document.
Kaz Kylheku <[email protected]> writes:
On 2024-01-25, Tim Rentsch <[email protected]> wrote:
Your understanding of the term "implementation-defined" is flawed.
No it isn't.
Obviously, an implementation cannot vaccilate among different
choices of how malloc(0) should behave. [...]
I think you've lost track of what point it is you are trying
to make. I know I have.
On 2024-01-25, Tim Rentsch <[email protected]> wrote:
Your understanding of the term "implementation-defined" is flawed.
No it isn't.
Obviously, an implementation cannot vaccilate among different choices of
how malloc(0) should behave.
On 2024-01-26, Tim Rentsch <[email protected]> wrote:
Kaz Kylheku <[email protected]> writes:
On 2024-01-25, Tim Rentsch <[email protected]> wrote:
Your understanding of the term "implementation-defined" is flawed.
No it isn't.
Obviously, an implementation cannot vaccilate among different
choices of how malloc(0) should behave. [...]
I think you've lost track of what point it is you are trying
to make. I know I have.
Here I'm simply defending myself against the accusation that I
don't understand the definition of implementation-defined
behavior. (Based on what I'm guessing might be the basis of the
perceived misunderstanding.)
This is a mostly self-contained topic, which has a connection to
an earlier point, that point being a relatively minor.
I walked into it like you wanted me to, executing your plan
to disconnect.
Tim Rentsch <[email protected]> writes:
Richard Kettlewell <[email protected]d> writes:
C's history since standardization does include a disregard for the
costs born by its users, yes.
I don't think that's true. Probably it is true that the
priorities of folks who have worked on the ISO C committee
don't exactly match your priorities.
The practical impacts and the complaints have been widespread for many
years. It?s not just me.
It is thoroughly unenlightening as an explanation.
Oh. I suppose that depends on what it is one thinks the writing
is trying to explain.
You were the one who recommend it in the context of this thread...
It claims to be trying to explain the treatment of null pointers and zero-length allocations. You may have to take it on trust, but for
someone who happens not to like the treatment chosen, it singularly
fails to explain that.
* The example includes no attempt at error handling, which is
where the pain point from the permission for malloc(0)=NULL
arises. So it seems like the author of that section didn't
consider the impact of the rules adopted.
The code pattern shown is something that was observed to be in
widespread use, not something that the Rationale's authors
constructed to illustrate a point. It isn't surprising that
there isn't any mention of error handling, because it isn't
important to what the section is trying to explain.
The implication is that error handling (and its relevance to reliability
and robustness) was not of interest.
* Nothing about the example depends either on malloc(0)=NULL or
malloc(0)!=NULL, making it mysterious why the example is said
to support the decisions made in C89.
Nothing in the Rationale document says that the given code
pattern supports the decisions made in the C standard. Any
implication actually goes the other direction, that decisions
about how malloc() works were guided (in part) by a desire to
support the given code pattern.
The point is alternative decisions (for example, the V7 behavior) would
have supported it just as well.
* The principle that 'zero-length objects' are invalid does not
seem to be rationalized anywhere in the text. While there may
be some advantage to it, nobody wrote them down.
What the text actually says is that the C89 Committee decided not
to accept the idea of zero-length objects. Probably the phrasing
could have been better; in any case what is meant is that they
decided not to /require/ support of zero-length objects, but
rather have it be optional (and implementation-defined).
The text describes requiring zero-length objects as a ?compelling
theoretical disadvantage?, without explaining why it?s so compelling or
even why requiring them would be a disadvantage. Earlier implementations apparently didn?t find any difficulty with them, or at least
insufficient difficulty to influence the rules for malloc.
A couple of other notes:
* The Unix V7 rules for malloc neatly bypass any concerns about
zero-length objects (if anybody cared about that at the time);
the definition is "returns a pointer to a block of at least size
bytes". malloc(0) returning a pointer to a 1-byte block would be
a non-issue under this definition regardless of any theoretical
objections.
I don't know anything about what Unix V7 says about malloc(), so
I have nothing to say about that.
You know what I told you above.
* If it happens that c=0 at any point in the loop, the new C23
rules make the example in 7.20.3 undefined behavior. I guess
the example has fallen out of favour.
Invalidating working code is a significant part of why people
object to the decision to make zero-sized realloc() calls be
undefined behavior.
The rationale notes that the C89 rules change the behavior of existing
code (despite ?existing code is important, existing implementations are
not? and ?avoid quiet changes?).
Finally back to the relationship to malloc:
Actually the two decisions have essentially nothing to do with
each other.
It does seem like nobody considered the question of whether the
two decisions were compatible; [...]
They may or may not have. You may want to read the Introduction
section of the Rationale document.
Being more specific about what you?re getting at might cause me to want
to read that in more detail. So far I?ve just identified a couple of ?important principles? that were violated by the rules C89 introduced
for malloc.
David Brown <[email protected]> writes:
On 24/01/2024 04:28, Tim Rentsch wrote:
Keith Thompson <[email protected]> writes:
Tim Rentsch <[email protected]> writes:
Keith Thompson <[email protected]> writes:
I got a response from JeanHeyd Meneide.
If realloc(ptr, 0) returns a null pointer there's no way to tell
whether allocation failed (and ptr has not been freed), or the
implementation returns a null pointer for zero-sized allocations
(and ptr has been freed). Some implementations set errno, but C
doesn't require it.
To avoid any confusion, I wrote the above paragraph; JeanHeyd Meneide
(the editor for C23 standard) did not.
| Sysop: | Keyop |
|---|---|
| Location: | Huddersfield, West Yorkshire, UK |
| Users: | 715 |
| Nodes: | 16 (2 / 14) |
| Uptime: | 149:03:43 |
| Calls: | 12,091 |
| Calls today: | 4 |
| Files: | 15,000 |
| Messages: | 6,517,565 |