Pointer to small amount of storage

S

santosh

Keith said:
Chuck, I'm sorry to say that you persist in missing the point.

``*q'' is not of any concern if we never evaluate it.

This was a technical question about whether the behavior of

long *q = malloc(2);

(assuming sizeof(long) > 2, and assuming malloc() returns a non-null
pointer) is defined. Your statement that it's not useful is
absolutely correct, and absolutely irrelevant to the question. The
standard requires the pointer returned by malloc() to be "suitably
aligned". The question is, what does this mean if the allocated space
isn't big enough? You seem to be trying to answer some other
question.

I suppose it would be okay until an attempt is made to write through the
pointer, at which point undefined behaviour might be invoked.

At least this is what seems sensible to me.

<snip>
 
K

Keith Thompson

Old Wolf said:
Old said:
Is there undefined behaviour here:
#include <stdlib.h>
int main()
{
short *p = malloc( sizeof *p );
long *q = (long *)p;
q;
return 0;
}

All's well. 6.3.2.3p7 tells us:

"A pointer to an object or incomplete type may be
converted to a pointer to a different object or
incomplete type. If the resulting pointer is not
correctly aligned for the pointed-to type, the
behavior is undefined. Otherwise, when converted
back again, the result shall compare equal to the
original pointer. [...]"

Good catch. How about this case:
long *q = malloc(2);

assuming sizeof(long) > 2 ? Now the 'defence' of
the pointer having to be able to be converted
back to short, is no longer available.

Here's (part of) what the standard says about the pointer returned by
malloc:

The pointer returned if the allocation succeeds is suitably
aligned so that it may be assigned to a pointer to any type of
object and then used to access such an object or an array of such
objects in the space allocated (until the space is explicitly
deallocated).

(The C90 wording is the same.)

I've always assumed that this meant that the result of malloc() can be
assigned to an object of any pointer-to-object type. For example,
suppose that type int is 4 bytes and requires strict alignment, and
assigning a misaligned void* to an int* causes a trap (even if it's
never dereferenced). Then ``int *ptr = malloc(1);'' will never cause
a trap, even though attempting to evaluate *ptr would invoke undefined
behavior.

I still think that's the intent, but look at what follows the "may be
assigned" stuff:

... and then used to access such an object or an array of such
objects in the space allocated ...

Clearly, given ``int *ptr = malloc(1);'', you *can't* use the result
to "access such an object". (One could argue that there's a
zero-length array, but C really doesn't support zero-sized objects, so
I don't think that argument holds up.)

I suspect that the author(s) of that section just didn't think about
the possibility of assigning the result of malloc to a pointer to a
type bigger than the allocated object, or perhaps didn't want to
expend the effort to craft the wording necessary to describe a case
that really isn't useful.

It's not immediately obvious whether the behavior is defined or not,
but IMHO it would be no great loss if it were undefined.
 
F

Flash Gordon

Keith Thompson wrote, On 28/02/08 07:18:

<snip question of whether pointer returned by malloc must be suitably
alligned for data which would not fit in the space>
Clearly, given ``int *ptr = malloc(1);'', you *can't* use the result
to "access such an object". (One could argue that there's a
zero-length array, but C really doesn't support zero-sized objects, so
I don't think that argument holds up.)

In terms of malloc it does since malloc(0) is allowed to return a
non-null pointer and at the very least that is a pointer to a zero
length array of (signed/unsigned/plain) char and it could reasonably be
considered to be a pointer to a 0 length array of any type.
I suspect that the author(s) of that section just didn't think about
the possibility of assigning the result of malloc to a pointer to a
type bigger than the allocated object, or perhaps didn't want to
expend the effort to craft the wording necessary to describe a case
that really isn't useful.

I agree that one of those is quite likely.
It's not immediately obvious whether the behavior is defined or not,
but IMHO it would be no great loss if it were undefined.

For the degenerate case of malloc(0) it could be an inconvenience on
occasion. Suppose you have some code of the form...

ptr = malloc(count * sizeof *ptr);
for (i=0; i<count; i++) {
/* do stuff using ptr */
}
free(ptr);

If you cannot safely assign the result returned by malloc because you
could not legally dereference it then you have to special case for
count==0, if you can then it will just work by magic with no need for a
special case.
 
C

CBFalconer

Flash said:
.... snip ...

For the degenerate case of malloc(0) it could be an inconvenience
on occasion. Suppose you have some code of the form...

ptr = malloc(count * sizeof *ptr);
for (i=0; i<count; i++) {
/* do stuff using ptr */
}
free(ptr);

If you cannot safely assign the result returned by malloc because
you could not legally dereference it then you have to special
case for count==0, if you can then it will just work by magic
with no need for a special case.

Covered in the standard. From N869:

7.20.3 Memory management functions

[#1] The order and contiguity of storage allocated by
successive calls to the calloc, malloc, and realloc
functions is unspecified. The pointer returned if the
allocation succeeds is suitably aligned so that it may be
assigned to a pointer to any type of object and then used to
access such an object or an array of such objects in the
space allocated (until the space is explicitly freed or
reallocated). Each such allocation shall yield a pointer to
an object disjoint from any other object. The pointer
returned points to the start (lowest byte address) of the
allocated space. 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. The value of a pointer <
that refers to freed space is indeterminate.
 
K

Keith Thompson

Flash Gordon said:
Keith Thompson wrote, On 28/02/08 07:18:
<snip question of whether pointer returned by malloc must be suitably
alligned for data which would not fit in the space>


In terms of malloc it does since malloc(0) is allowed to return a
non-null pointer and at the very least that is a pointer to a zero
length array of (signed/unsigned/plain) char and it could reasonably
be considered to be a pointer to a 0 length array of any type.

If malloc(0) returns a non-null pointer, then "the behavior is as if
the size were some nonzero value, except that the returned pointer
shall not be used to access an object" (C99 7.20.3p1). It may well be
that that behavior matches the behavior that a pointer to the
(nonexistent) first element of a zero-length array *would* have if the
standard actually talked about zero-length arrays, but it wouldn't be
easy to prove it.

And, of course, an implementation can choose to always return a null
pointer for malloc(0).

The authors of the standard *could* have defined a non-null value
returned by malloc(0) in terms of a zero-length array. Either it
didn't occur to them, or they specifically chose not to, likely
because they didn't want to open that particular can of worms.

I'd be pleased if the standard did treat zero-length arrays, and
perhaps zero-sized structs, consistently. But note that zero-length
arrays, and other zero-sized objects, could introduce all sorts of
confusion. Distinct objects, or array elements, or struct members,
could have the same address; distinct struct members could have the
same offset.

[...]
For the degenerate case of malloc(0) it could be an inconvenience on
occasion. Suppose you have some code of the form...

ptr = malloc(count * sizeof *ptr);
for (i=0; i<count; i++) {
/* do stuff using ptr */
}
free(ptr);

If you cannot safely assign the result returned by malloc because you
could not legally dereference it then you have to special case for
count==0, if you can then it will just work by magic with no need for
a special case.

If you can't assign the result of malloc(), it's not "because you
could not legally dereference it"; after all, it's clear that you can
assign either a null pointer or a pointer just past the end of an
array, even though you can't dereference either of those.

Again, quoting C99 7.20.3p1:

The pointer returned if the allocation succeeds is suitably
aligned so that it may be assigned to a pointer to any type of
object and then used to access such an object or an array of such
objects in the space allocated (until the space is explicitly
deallocated).

I think it's *intended* that (assuming sizeof(int) > 1):

int *ptr;
*ptr = malloc(sizeof(int)/2);

has defined behavior. Assuming malloc() succeeds, the result is a
pointer that's properly aligned for type int, but that points to a
space that's too small to hold an int.

The assignment certainly won't blow up due to misalignment. But the
the standard (a) incorrectly assumes that *dereferencing* a properly
aligned pointer is ok (it's clearly not if the space it points to is
properly aigned but too small) and thereby (b) fails to guarantee that
just *assigning* such a pointer is ok.

To clarify point (a), the standard says the pointer "is suitably
aligned so that it may be ... used to access such an object". This
implies that suitable alignment is enough to guarantee this; it isn't.

It's easy to imagine an implementation in which assigning a pointer
that's properly aligned, but that doesn't point to a sufficiently
large space, can cause a trap. The standard doesn't do enough to
forbid such a trap in this particular case (though it might be a
useful check in some other cases).

Suggested wording:

The pointer returned if the allocation succeeds is suitably
aligned so that it may be assigned to a pointer to any type of
object. If the space allocated is sufficiently large, the pointer
may then be used to access such an object or an array of such
objects in the space allocated (until the space is explicitly
deallocated).

(Perhaps the authors thought that the "sufficiently large" requirement
was so obvious that it didn't need to be stated.)

Note that in the above, including both quotes from the standard and my
own wording, the term "pointer" refers to a pointer *value*, not a
pointer object, and the alignment of a pointer refers to the alignment
of the object it points to, not to the alignment of a pointer object.
 
C

christian.bau

Is there undefined behaviour here:

#include <stdlib.h>

int main()
{
  short *p = malloc( sizeof *p );
  long *q = (long *)p;
  q;

  return 0;

}

Rationale being that evaluating q causes undefined
behaviour because q is neither null, nor pointing to storage
suitable for an object of type *q.

Assuming so; then is it undefined before the `q;' line, or
is the code valid and q indeterminate?

Remember that in C99 an implementation can use two possible strategies
to implement malloc (0): Either the implementation allows returns a
null pointer, _or_ the implementation tries to return malloc (1), and
only returns a null pointer in the rare case that malloc (1) fails.

So in C99, the harmless

long *p = malloc (0);

would on some implementations usually produce exactly the same
situation that you are asking about. I would assume that the behaviour
must be defined, otherwise C99 is in serious trouble.
 
D

David Thompson

I think you've hit on an ambiguity in the standard.
Casting from a short * to a long * is not guaranteed to work. The short may
be improperly aligned, and so you could generate some sort of runtime error.

However the return from malloc() is guaranteed to be aligned for any
purpose. That leaves open the question of whether a too small allocation
can be assigned to the pointer. We could say it is OK because an allocation
of zero bytes can be assigned. However the zero allocation could fall under
the "one past" rule, in which case we are back to allowing the behaviour to
be undefined.

DR075 to C90 affirmed that the space allocated (and pointer returned)
by malloc and friends must be aligned for _any_ object type, even if
it's not big enough to actually store such an object. The wording to
which this applied did not change in C99 or AFAICS the TCs thereto.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 
D

David Thompson

I think it's *intended* that (assuming sizeof(int) > 1):

int *ptr;
*ptr = malloc(sizeof(int)/2);
Nit: I'm certain you didn't mean the star on the second line.
has defined behavior. Assuming malloc() succeeds, the result is a
pointer that's properly aligned for type int, but that points to a
space that's too small to hold an int.
<snip>
- formerly david.thompson1 || achar(64) || worldnet.att.net
 
K

Keith Thompson

David Thompson said:
Nit: I'm certain you didn't mean the star on the second line.

Quite right, thanks. (I may have first written it as an initialized
declaration, then split it into an uninitialized expression and an
assignment.)
 

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,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top