Pointer to small amount of storage

O

Old Wolf

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?
 
V

vippstar

Is there undefined behaviour here:

#include <stdlib.h>

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

return 0;

}


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

No, q becomes a dangling pointer (*IF* malloc does not return NULL)
after the assignment.
 
M

Malcolm McLean

Old Wolf said:
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?
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.
 
E

Eric Sosman

Old said:
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?

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. [...]"

Since the value returned by malloc() is either NULL (in which
case the conversions certainly work) or correctly aligned for
any possible data type (7.20.3p1), the "otherwise" holds. If
`long *q = (long*)p;' were followed by `short *r = (short*)q;',
we would therefore have `r == p'. The requirement for `r == p'
could not hold if undefined behavior had occurred somewhere along
the way; therefore, there's no U.B.

Also, the evaluation `q;' produces no U.B. The fact that
a pointer is not dereferenceable does not imply that the pointer
value is indeterminate; the values NULL and "one past the end"
are examples of perfectly good non-dereferenceable pointers.
 
C

CBFalconer

Malcolm said:
.... snip ...

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.

No. The NULL, if returned from a zero allocation, is already one
past the assigned storage. It cannot be dereferenced.

The simplest method is not to allow a zero allocation to be made.
If you have access to the system code, and it does this, change it
to read:

void *malloc(size_t sz) {

if (!sz) sz++;
... the rest of the code ...
}

otherwise interpose something to do the same.
 
M

Malcolm McLean

CBFalconer said:
No. The NULL, if returned from a zero allocation, is already one
past the assigned storage. It cannot be dereferenced.
It's one past the last element of a zero-element array. Arguably.
 
O

Old Wolf

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.
 
E

Eric Sosman

Old 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.

But it can still be converted back to void* and must
then compare equal to the value of malloc():

void *v = malloc(2);
long *q = v;
assert ((void*)q == v);

.... so the argument still holds. Or so it seems to me.
 
C

CBFalconer

Old said:
.... snip ...

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.

That malloc doesn't assign enough space to hold the posited long.
Replace the '2' with "sizeof *q" (sans quotes) and all is well.
 
H

Harald van Dijk

That malloc doesn't assign enough space to hold the posited long.

That's the point. And the assignment's fine, since malloc's result is
properly aligned.
 
F

Flash Gordon

CBFalconer wrote, On 24/02/08 00:56:
Old Wolf wrote:
... snip ...

That malloc doesn't assign enough space to hold the posited long.

Which was the point of the value selected and the stated assumption.
Replace the '2' with "sizeof *q" (sans quotes) and all is well.

I somehow suspect that Old Wolf knows that.
 
C

CBFalconer

Harald said:
That's the point. And the assignment's fine, since malloc's
result is properly aligned.

I see no point. And the assignment is NOT fine, since it writes
into memory it doesn't own.
 
H

Harald van Dijk

And the assignment is NOT fine, since it writes into
memory it doesn't own.

The initialisation (I mistakenly called it assignment) does not write to
anything except q. *q is not accessed at all.
 
R

Richard Tobin

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.
[/QUOTE]
That malloc doesn't assign enough space to hold the posited long.
Replace the '2' with "sizeof *q" (sans quotes) and all is well.

There isn't any "posited long". The question is whether this is legal
*if you don't store into q*.

Bear in mind that long *q = malloc(0) is legal.

-- Richard
 
K

Kaz Kylheku

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.

However, I don't think that a malloc of, say, four bytes, is required
to have a stricter alignment than four bytes. No addressable object in
C has a stricter alignment requirement than its own size.

A malloced block of four bytes clearly cannot be used for any purpose
whatsoever, but only for those purposes which can be satisified by a
four byte block. Why should it be aligned for purposes to which it is
not suited?
 
E

Eric Sosman

Kaz said:
However, I don't think that a malloc of, say, four bytes, is required
to have a stricter alignment than four bytes. No addressable object in
C has a stricter alignment requirement than its own size.

This was debated donkey's years ago in a slightly different
guise: Could calloc(64,2) return memory that satisfied a two-byte
alignment but nothing stricter? After a good deal of debate, the
consensus answer was that Standard's "is suitably aligned so that
it may be assigned to a pointer of any type" does not imply that
the resulting pointer can be dereferenced. The resulting pointer
could, for example, be compared for equality with a different (and
"proper") pointer of the same type, and the comparison would have
to work; the [x]alloc() result could not be "damaged" by assignment
to any pointer type whatsoever.
A malloced block of four bytes clearly cannot be used for any purpose
whatsoever, but only for those purposes which can be satisified by a
four byte block. Why should it be aligned for purposes to which it is
not suited?

"Because the Standard says so." It's a little bit like the
obstreperous child's "Why do I have to brush my teeth? I don't
want to!" being answered by "Because I'm Your Mother, So Do It."
 
R

Richard Tobin

A malloced block of four bytes clearly cannot be used for any purpose
whatsoever, but only for those purposes which can be satisified by a
four byte block. Why should it be aligned for purposes to which it is
not suited?
[/QUOTE]
"Because the Standard says so."

But it's often been suggested that the as-if rule applies here.

If the only effect of misalignment is to prevent you dereferencing,
and it's not big enough to hold a type whose dereferencing would
fail, then you can't tell the difference.

On the other hand, if misalignment causes a trap when you load
the value into a register, you might well be able to.

-- Richard
 
E

Eric Sosman

"Because the Standard says so."

But it's often been suggested that the as-if rule applies here.

If the only effect of misalignment is to prevent you dereferencing,
and it's not big enough to hold a type whose dereferencing would
fail, then you can't tell the difference.

On the other hand, if misalignment causes a trap when you load
the value into a register, you might well be able to.[/QUOTE]

Right. So on a machine where pointers to different data
types "smell the same" -- are losslessly interconvertible and
so on -- [x]alloc() can get away with murder. But if the
machine uses different representations for T1* and T2* such
that conversion from either to the other loses information,
[x]alloc() must play by the book.

In the case at hand, where sizeof(long) > 2 by assumption,

long *ptr = malloc(2);

.... must not lose information: free(ptr) has to work. So if
malloc() is playing games with sizes, it must have knowledge
that its un-aligned result behaves no differently than would
an aligned result, so long as it's not dereferenced.
 
C

CBFalconer

Richard said:
There isn't any "posited long". The question is whether this is
legal *if you don't store into q*.

Yes there is. Its name is '*q'. It is uninitialized, and not
usable until initialized. Initializing would be illegal, because
the storage is too small. I guess the word 'posited' was not clear
to non-English speakers.
Bear in mind that long *q = malloc(0) is legal.

But totally useless, for the same reasons.
 
K

Keith Thompson

CBFalconer said:
Yes there is. Its name is '*q'. It is uninitialized, and not
usable until initialized. Initializing would be illegal, because
the storage is too small. I guess the word 'posited' was not clear
to non-English speakers.


But totally useless, for the same reasons.

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'll probably post some thoughts on the actual original question
later.
 

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

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top