Re: (part 10) More Schildt-like errors in Dicky Heathen's book
- From: Ben Bacarisse <ben.usenet@xxxxxxxxx>
- Date: Tue, 20 Jan 2009 00:31:32 +0000
blargg.ei3@xxxxxxxxxxxxx (blargg) writes:
Han from China - Master Troll wrote:<snip>
CBFalconer wrote:
[...]<snip>
void *mymalloc(size_t sz) {
void *p;
p = malloc(sz);
if (sz || p) return p;
else /* we have the 0 size and NULL returned case */
return malloc(1);
}
This will prevent any actual malloc(0) on systems that return NULL.
Nonsense. The second case above reveals that your function will still act
like malloc(0) if malloc(0) returns a non-NULL pointer. Then that pointer
is useless for the user.
That mymalloc function above seems useful to me. It guarantees that
mymalloc(sz) will return a non-NULL pointer on success, and NULL on
failure, so the calling code can always treat NULL as an error, even
when sz is zero. If the result is non-NULL, the caller can access all
bytes at offsets in the range [0,sz). When it's done, it muse free the
pointer.
I am now inclined to agree that this usage has some merit, but since I
originally posted a comment similar to Han's I feel I should explain
why I have changed position. It was my belief that both NULL and an
unusable non-NULL pointer can be used to access bytes in the range
[0,sz) because that interval is empty. However, I am no longer so
sure about this...
You can do some diddling to make the routines avoid the double call
I wouldn't call it diddling. Your code is functionally equivalent to the
following:
void *mymalloc(size_t sz)
{
return malloc(sz ? sz : 1);
}
If one is using a debugging malloc which checks that all accesses are
within the block's bounds, the first code will work smoothly with it
even for a zero-length block. By always trying malloc(sz) first, even
when sz is zero, it gives the debugging allocator an opportunity to
return non-NULL for a zero-length block. If the above code instead
simply returned malloc(sz ? sz : 1), it would be lying to the
debugging allocator, and allow user code to access the first byte
without being flagged.
This is good point but it is an argument against the simpler
alternative not in favour of the more complex mymalloc. If one leaves
the malloc call alone, all accesses can often be checked (because with
many debugging systems accesses though NULL are caught as well). If
the debugging allocator happens to return NULL for malloc(0) then
mymalloc looses you the checking advantage (by allocating 1). I
suspect that, in general, you get the best error checking when malloc
calls are left alone.
except in case 2. My case 2 doesn't leave the user with a non-NULL pointer
that can't be used. There is only one malloc() call, and I wouldn't say my
ternary operator gives me any worse performance than your if statement.
But this gives it a more consistent interface to the user. With
malloc, user code must do this:
char* p = malloc( sz );
if ( p == NULL && sz != 0 )
return 1;
This is certainly true, though there is a case to be made for
documenting, at the point of use, that a zero-size allocation is fine
(but malloc may give you a NULL). Of course it all depends on this
last point of yours:
<snip>
And then if you want to iterate over its contents using a pointer to
the end, I think you must have a special case again (I think adding
zero to a null pointer is UB):
This caused me to re-think. I had always assumed that the code below:
if ( sz > 0 )
{
char* end = p + sz;
char* pos;
for ( pos = p; pos < end; ++pos )
*pos++ = 'X';
}
did not need the 'if' because, when p was a null-pointer, adding zero
to it gave you a null pointer back. I can't support that with quotes
from the standard, so I have been looking through Defect Reports.
There does not seem to be any that clarify this matter, but one of
them (DR#54 [1]) backs up your view whilst not addressing the issue
directly. It states that functions like memcpy may be passed zero
sizes but that the other parameters must still be valid. (It refers to
section 7.1.7 but surely it means 7.1.4 which deals with valid library
function arguments.) Null pointers are only valid when the function
description explicitly states that they are, and memcpy's description
does not. I.e. memcpy(p, src, sz); is UB if p == NULL even when sz ==
0.
To sum up, I think you are right. UB awaits lots of innocent-looping
code that uses a potentially null return from malloc with a zero size.
This there is some value in forcing a non-null return when allocating
a size of zero because not all code that uses the result will
degenerate to a safe no-op when the size is zero.
This is why I like to read c.l.c.
[1] http://std.dkuug.dk/JTC1/SC22/WG14/www/docs/dr_054.html
<snip>
--
Ben.
.
- References:
- Re: (part 10) More Schildt-like errors in Dicky Heathen's book
- From: CBFalconer
- Re: (part 10) More Schildt-like errors in Dicky Heathen's book
- From: Han from China - Master Troll
- Re: (part 10) More Schildt-like errors in Dicky Heathen's book
- From: blargg
- Re: (part 10) More Schildt-like errors in Dicky Heathen's book
- Prev by Date: Re: struct problems
- Next by Date: Re: (part2) Han from China teaches you C
- Previous by thread: Re: (part 10) More Schildt-like errors in Dicky Heathen's book
- Next by thread: Re: (part 10) More Schildt-like errors in Dicky Heathen's book
- Index(es):