call to malloc with size 0

M

Method Man

Ben Pfaff said:
Dereferencing the result of malloc(0) yields undefined behavior.

How can you say for certain that it will yield UB? Assuming the pointer
obtained from malloc(0) is unique and valid, it therefore must be pointing
to some valid memory location. I thought only invalid and void pointers
caused UB when dereferenced. Perhaps I am misunderstanding something.
What problem do you envision with type-casting it?

I was brainstorming. I suppose there wouldn't be any problems.
 
J

J. J. Farrell

Neo said:
invalid cast - what do you mean by saying *invalid cast*.

He means what he explained earlier. Here's another try:

Your code says that malloc() returns an int. Your cast has no well-
defined meaning, and prevents the compiler putting out an error
message that would have helped remind you that you hadn't included
the header file that declares malloc() properly.
isnt it a standard
practice to cast the pointer returned by the malloc() to desired type?

It's a common practice, and most knowledgeable C coders regard it
as a bad practice in all but a few special cases.
or how then it can be otherwise like:
p = malloc(0);
???

Yes. Does that surprise you? If you just take the cast out, you'll
get an error message. If you then include <stdlib.h> as you should
do when using malloc(), the error message will go away. The is because
the header file correctly declares malloc() to return a <void *>
which can be automatically converted to a <char *> by assignment.
 
M

Mark McIntyre

How can you say for certain that it will yield UB?

7.20.3 Memory management functions
"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."
So either you have as null pointer, or an unusable one.
Assuming the pointer obtained from malloc(0) is unique and valid,

it can't be - see above quote - except in the case where its a null
pointer, which is not dereferencable anyway.
 
B

Ben Pfaff

Method Man said:
How can you say for certain that it will yield UB?

Undefined behavior has to do with what the standard says. It
doesn't have to do with what happens in real life. In this case,
the standard says "the returned pointer shall not be used to
access an object," so an attempt to dereference it yields
undefined behavior.
Assuming the pointer obtained from malloc(0) is unique and
valid, it therefore must be pointing to some valid memory
location. I thought only invalid and void pointers caused UB
when dereferenced. Perhaps I am misunderstanding something.

You can't dereference a void pointer. You have to convert it to
a pointer to some complete type first.
 
D

Default User

Method said:
How can you say for certain that it will yield UB?


Because the standard says the returned pointer shall not be used to
access an object.




Brian
 
K

Keith Thompson

Craig Barkhouse said:
Are successive calls to malloc(0) guaranteed to return unique values,
though? (Assuming they don't return NULL.) Given that actually
dereferencing such the pointer invokes undefined behaviour, couldn't a
malloc() implementation set aside a certain value that it always returns
when size is 0? It might have to increment a reference count that would
later get decremented by free().

Yes, I believe it has to be unique. The wording in the standard is:

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.

Since the behavior if the size is nonzero is to return a unique
pointer (ignoring recycling of free()d pointers), each call to
malloc(0) has to return a unique pointer (if it doesn't return a null
pointer).
 
K

Keith Thompson

Or, if you prefer macros:

#define XMALLOC(size) malloc(size == 0 ? 1 : size)
#define YMALLOC(size) (size == 0 ? NULL : malloc(size))

Your XMALLOC is probably clearer than the one I came up with, but I'm
going to show it off because I think it's kinda clever.

#define XMALLOC(size) malloc((size) + ((size)==0))

(Yes, I habitually parenthesize references to arguments to
function-like macros; it's easier than figuring out when it isn't
necessary.)
 
X

xarax

Keith Thompson said:
Yes, I believe it has to be unique. The wording in the standard is:

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.

Since the behavior if the size is nonzero is to return a unique
pointer (ignoring recycling of free()d pointers), each call to
malloc(0) has to return a unique pointer (if it doesn't return a null
pointer).

This is perfectly acceptable behavior. There are architectures
that have unusable memory ranges within the total possible range
that could be represented by a (void*). A conforming implementation
could choose an available unique value from the unusable range to
return from malloc()/calloc() when the requested size is zero. A
later free() would accept that unusable pointer.

Although I think it would be simpler to just return null, rather
than keeping track of the "unusable" pointers returned by malloc(0)
for later free().

Even better would be for the programmer to verify that his
processing doesn't request zero bytes nor attempt to free
a null pointer value, but that's just how I like to program...eek!
 
K

Keith Thompson

xarax said:
This is perfectly acceptable behavior. There are architectures
that have unusable memory ranges within the total possible range
that could be represented by a (void*). A conforming implementation
could choose an available unique value from the unusable range to
return from malloc()/calloc() when the requested size is zero. A
later free() would accept that unusable pointer.

The simplest way for malloc() to return a unique pointer for each
malloc(0) would be for it to change the argument internally to 1:

void *malloc(size_t size)
{
if (size == 0) size = 1;
/* the rest of the function */
}

Even this isn't necessary if the uniqueness is a natural consequence
of the algorithm (i.e., 0 might not have to be treated as a special
case).
 
A

Al Bowers

Keith said:
The simplest way for malloc() to return a unique pointer for each
malloc(0) would be for it to change the argument internally to 1:

void *malloc(size_t size)
{
if (size == 0) size = 1;
/* the rest of the function */
}

There's no guarantee that this will return a unique pointer.
Even a malloc of size 1 may fail if the memory resources are
exhausted.
 
K

Keith Thompson

Al Bowers said:
There's no guarantee that this will return a unique pointer.
Even a malloc of size 1 may fail if the memory resources are
exhausted.

Which, again, is perfectly acceptable. If the implementation decides
to have malloc(0) return a unique pointer on each call, it can't do so
indefinitely; eventually it will fail by running out of memory, and it
will indicate the failure by returning a null pointer.

The standard (C99 7.20.3p1) says:

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.

Making malloc(0) equivalent to malloc(1) satisfies this.
 
D

Dan Pop

How can you say for certain that it will yield UB? Assuming the pointer
obtained from malloc(0) is unique and valid, it therefore must be pointing
to some valid memory location.

It's the same as with pointers pointing one past the last element of an
array. They're "valid" only in the sense that assigning, converting or
comparing them for equality works normally, but they cannot be
dereferenced. Whether they point to a valid memory location is up to
the implementor (have a look at Electric Fence to see how an
implementation can create invalid memory locations having valid memory
addresses).
I thought only invalid and void pointers
caused UB when dereferenced. Perhaps I am misunderstanding something.

If the standard says that a pointer value cannot be used to access an
object, attempting to do so results in undefined behaviour. This is
different from an invalid pointer value, which cannot be used at all
without invoking undefined behaviour (except for examining its
representation on a byte by byte basis).

E.g.

char *p = malloc(0);
char *q = malloc(1);
free(q);

At this point, assuming that both malloc calls returned non-null pointers,
p contains a valid pointer value that cannot be dereferenced, while q
contains an invalid pointer value, which cannot be used at all without
invoking undefined behaviour.

Void pointers are in a different category: the attempt to dereference
them is a constraint violation, so a diagnostic is required.

Dan
 
A

Al Bowers

Keith said:
Which, again, is perfectly acceptable. If the implementation decides
to have malloc(0) return a unique pointer on each call, it can't do so
indefinitely; eventually it will fail by running out of memory, and it
will indicate the failure by returning a null pointer.

The standard (C99 7.20.3p1) says:

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.

Making malloc(0) equivalent to malloc(1) satisfies this.

Yes, your internal implement satisfies the Standard requirements. Either
a NULL value is returned or a unique pointer value. Your implement
is weighed to the latter.

I was just questioning the wording in your description.
Specifically in the comment:

"The simplest way for malloc() to return a unique pointer for each
malloc(0) would be for it to change the argument internally to 1"

I was equating your word "each" to mean "all".

At some point the implement may be unable to return
a unique pointer(it returns NULL), which means that one cannot
say that every call to malloc(0) will return a unique pointer.
 
F

Flash Gordon

Method Man <[email protected]> scribbled the following:


If the C memory model was continuous rather than discrete, then there
would be no problem. In a continuous memory model, pointer values
would only serve as "starting points" of allocated memory, not
allocated memory itself. If you think of the conventional discrete
memory model as labelling boxes with addresses, in a continuous memory
model you don't label the boxes, you label the gaps between them. This
way the malloc() implementation could safely return the same,
non-null, pointer value over and over again from calls to malloc(0),

I don't think this would be allowed. I think that any valid pointer (as
opposed to null pointer) returned by malloc must compare different to
any other object that still exists. I.e.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
void *p1 = malloc(0);
void *p2 = malloc(0);

if (p1 && (p1 == p2))
puts("This should never be printed!");

return 0;
}

Modulo any errors people point out (I've not tried it) I don't believe
the above is allowed to print "This should never be printed!" which it
would with the implementation you describe.
because reserving 0 bytes onward from that gap between boxes doesn't
actually reserve any box at all, and it's the boxes you store stuff
in, not the gaps between them. This is of course very much off-topic
for comp.lang.c but those who have studied real analysis and topology
know what I'm talking about.

I know what you are talking about, I just don't think it is allowed.

One possible way the library could implement malloc(0) returning a
non-null pointer is to treat it as malloc(1), I.e. give you a pointer
to a byte that it really has allocated. After all, a derefference isn't
guaranteed to fail by the standard.
 
K

Keith Thompson

Al Bowers said:
Yes, your internal implement satisfies the Standard requirements. Either
a NULL value is returned or a unique pointer value. Your implement
is weighed to the latter.

I was just questioning the wording in your description.
Specifically in the comment:

"The simplest way for malloc() to return a unique pointer for each
malloc(0) would be for it to change the argument internally to 1"

You're right, I should have qualified that.
 
F

Flash Gordon

On Thu, 18 Nov 2004 08:49:38 +0530

<snip not casting the return value of malloc.
invalid cast - what do you mean by saying *invalid cast*. isnt it a
standard practice to cast the pointer returned by the malloc() to
desired type? or how then it can be otherwise like:
p = malloc(0);
???

In C it is standard practice to not cast the value returned by malloc.
There are a few obscure exceptions, an one person who posts here has a
good reason why he (as opposed to everyone else) has to.

If you use a function without a prototype the compiler assumes it
returns an int where as malloc actually returns void*. Thus if, for
example, pointers are returned in A0 (an address register) and ints are
returned in D0 (a data register) then the compiled version of your code
will ignore the pointer returned by malloc and instead convert some
random value that happens to be in D0 to a pointer, this is obviously
likely to cause your program to fail, probably when your boss is
demonstrating the program to a very big potential customer. Such is the
rath of invoking undefined behaviour.

If you don't have the cast and you have forgotten to include stdlib.h
then the compiler is *required* to generating a diagnostic (C99 is
different and a diagnostic is required even with the cast, but I'll bet
you are not using a*conforming* C99 implementation).

Conversion between void* and other object (not function) pointers is
automatic and does not require a cast.

The prefered (by most of the regulars) method of calling malloc around
here is
p = malloc( n * sizeof *p);

If you search the group you can find lots of discussion on this point.
 
F

Flash Gordon

Your XMALLOC is probably clearer than the one I came up with, but I'm
going to show it off because I think it's kinda clever.

#define XMALLOC(size) malloc((size) + ((size)==0))

(Yes, I habitually parenthesize references to arguments to
function-like macros; it's easier than figuring out when it isn't
necessary.)

Of course, either macro is a potential problem if you do
p = XMALLOC( size_array[i++] );
 
E

E. Robert Tisdale

Flash Gordon wrote:

Neo wrote:

<snip not casting the return value of malloc.
In C, it is standard practice to not cast the value returned by malloc.
Nonsense!

There are a few obscure exceptions, and one person who posts here
has a good reason why he (as opposed to everyone else) has to.

Lots of people who post here have good rerasons
to cast the pointer returned by the malloc(size_t).
If you use a function without a prototype

Don't do that.
the compiler assumes [that] it returns an int
where as malloc actually returns void*.
Thus if, for example, pointers are returned in A0 (an address register)
and ints are returned in D0 (a data register),
then the compiled version of your code
will ignore the pointer returned by malloc and instead
convert some random value that happens to be in D0 to a pointer,
this is obviously likely to cause your program to fail,
probably when your boss is demonstrating the program
to a very big potential customer.
Such is the rath of invoking undefined behaviour.
FUD

http://en.wikipedia.org/wiki/FUD

If you don't have the cast and you have forgotten to include stdlib.h
then the compiler is *required* to generating a diagnostic
(C99 is different and a diagnostic is required even with the cast,
but I'll bet you are not using a *conforming* C99 implementation).

Then you should get a C compiler that conforms with this requirement.
There are plenty of them them that do.
Conversion between void* and other object (not function) pointers
is [implicit and] automatic and does not require a cast.

The prefered (by most of the regulars) method
of calling malloc around here is

p = malloc( n * sizeof *p);

This is a matter of style and [poor] taste.

Write:

char* p = (char*)malloc(n*sizeof(char*));

and both C and C++ compilers will accept your code.
 
F

Flash Gordon

Lots of people who post here have good rerasons
to cast the pointer returned by the malloc(size_t).


Don't do that.

Agreed. I even explain below why not.
the compiler assumes [that] it returns an int
where as malloc actually returns void*.
Thus if, for example, pointers are returned in A0 (an address
register) and ints are returned in D0 (a data register),
then the compiled version of your code
will ignore the pointer returned by malloc and instead
convert some random value that happens to be in D0 to a pointer,
this is obviously likely to cause your program to fail,
probably when your boss is demonstrating the program
to a very big potential customer.
Such is the rath of invoking undefined behaviour.

FUD

http://en.wikipedia.org/wiki/FUD

It is entirely real. I've used a 68000 based system that returned
pointers in A0 and ints in D0. Also as you should be aware we are
getting new systems where int and pointers are not the same size,
leading to more systems where it would fail.
Then you should get a C compiler that conforms with this requirement.
There are plenty of them them that do.

Name 5 conforming C99 implementations. I'll start you off with gcc not
being one, since GNU explicitly states that it does not yet conform.
Conversion between void* and other object (not function) pointers
is [implicit and] automatic and does not require a cast.

The prefered (by most of the regulars) method
of calling malloc around here is

p = malloc( n * sizeof *p);

This is a matter of style and [poor] taste.

Write:

char* p = (char*)malloc(n*sizeof(char*));

and both C and C++ compilers will accept your code.

As discussed previously, with very few exceptions there is no good
reason to nead to compile C code as C++, especially since there are
various things which compile and behave differently.

You are, of course, trolling, I only respond so that newbies don't think
I accept your claims. Unless you can name 5 conforming C99
implementations (which should be easy if your claim that there are
plenty were true) then consider any lack of responce to further posts by
you to indicate that I disagree with you.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top