pointer conversion

J

junky_fellow

K&R say that,
It is guaranteed that 1) a pointer to an object may be converted to a
pointer to an object whose type requires less
or equally strict storage alignment and 2) back again without change.

My question is that, is it legal to dereference the intermediate
pointer (the pointer with less strict alignment that we get after
conversion) ? Does this intermediate pointer also point to the
same memory location as the pointer (with more strict storage
alignment) from which it was converted, of course, the type of
object pointed would be different.

For eg. Consider the following piece of code:
int main(void)
{
int arr[10];
int *i_ptr; /* has more strict storage alignment */
short *s_ptr; /* has less strict storage alignment as
compared to i_ptr */
i_ptr = arr + 4;
s_ptr = (short *)i_ptr; /* should be valid, since s_ptr has
less strict storage alignment
as compared to i_ptr */

}

Now, is this legal to deference s_ptr ?
Does s_ptr also point to the same memory location as i_ptr ? (of
course the type of objects they point to are different, and
dereferencing "s_ptr" may give different results on machines that
have different endianness )

I am asking this question because I think that "short *" might have
an entirely different representation as compared to "int *".
So, during converison does the complier take care of this so that
the "short *" obtained after convesrsion is perfectly valid and
it also points to the same meory location ?
 
B

baumann@pan

i think pointer is too simple as its meaning has.

pointer of object is the address of that object. the address can be in
memory, IO space etc.

you can image the object occupies sizeof( that object data type) mem/io
space , while the pointer tells you the start point.

if you cast a pointer as other data type, you' d better be sure the
original has the data type to be converted, then you can cast it to
other type(or plus/minus some offset).

say
int a[100];
defined in a fucntion, ie. local variable,

then it occupies 100 * 4 size statck space.
and assume a start from 0x10000

now we assign some value to the int array.

memset(a,0,100*sizeof(int));
a[0]=0x00010203;
a[1]=0x01020304;
a[2]=0x02030405;

now the stack layout from the address 0x10000 looks like( consider the
cpu is in b e mode)

0x10000 0x00
0x10001 0x01
0x10002 0x02
0x10003 0x03

0x10004 0x01
0x10005 0x02
0x10006 0x03
0x10007 0x04

0x10008 0x02
0x10009 0x03 <<---- i cast from the address to short *
0x1000A 0x04
0x1000B 0x05

if we dont consider the alignment,

we can cast 0x10009 as

short *p = (short*)(0x10009).

and get *p =0x0304;

the cast type tell you the length to cast. but its validity is in
charge by u.
 
M

Mark McIntyre

On 5 Jun 2005 23:48:00 -0700, in comp.lang.c ,
For eg. Consider the following piece of code:
int main(void)
{ ....
i_ptr = arr + 4;
s_ptr = (short *)i_ptr; /* should be valid, since s_ptr has
less strict storage alignment
as compared to i_ptr */

}

Now, is this legal to deference s_ptr ?

As far as I can see, yes. However as you say....
I am asking this question because I think that "short *" might have
an entirely different representation as compared to "int *".

.... the value you obtain might be quite different to what you expect.
For instance of ints and shorts were both little-enduan but 4 and 2
bytes respectively.

i wait for someone to correct me!
 
P

pete

K&R say that,
It is guaranteed that 1) a pointer to an object may be converted to a
pointer to an object whose type requires less
or equally strict storage alignment and 2) back again without change.

My question is that, is it legal to dereference the intermediate
pointer (the pointer with less strict alignment that we get after
conversion) ? Does this intermediate pointer also point to the
same memory location as the pointer (with more strict storage
alignment) from which it was converted, of course, the type of
object pointed would be different.

For eg. Consider the following piece of code:
int main(void)
{
int arr[10];
int *i_ptr; /* has more strict storage alignment */
short *s_ptr; /* has less strict storage alignment as
compared to i_ptr */
i_ptr = arr + 4;
s_ptr = (short *)i_ptr; /* should be valid, since s_ptr has
less strict storage alignment
as compared to i_ptr */

}

Now, is this legal to deference s_ptr ?
Does s_ptr also point to the same memory location as i_ptr ? (of
course the type of objects they point to are different, and
dereferencing "s_ptr" may give different results on machines that
have different endianness )

I am asking this question because I think that "short *" might have
an entirely different representation as compared to "int *".
So, during converison does the complier take care of this so that
the "short *" obtained after convesrsion is perfectly valid and
it also points to the same meory location ?

In general, the standard committee isn't too enthusiastic
about type punning static and automatic objects,
except as arrays of char.

Here's a thread about pointer conversions and values on

http://groups-beta.google.com/group...7380c/39ea1ff5e03a06ec?tvc=1#39ea1ff5e03a06ec
 
M

Me

My question is that, is it legal to dereference the intermediate
pointer (the pointer with less strict alignment that we get after
conversion) ? Does this intermediate pointer also point to the
same memory location as the pointer (with more strict storage
alignment) from which it was converted, of course, the type of
object pointed would be different.

int arr[10];
int *i_ptr; /* has more strict storage alignment */
short *s_ptr; /* has less strict storage alignment as
compared to i_ptr */
i_ptr = arr + 4;
s_ptr = (short *)i_ptr; /* should be valid, since s_ptr has
less strict storage alignment
as compared to i_ptr */

Now, is this legal to deference s_ptr ?

No, the aliasing rules (6.5/7 in the standard) say this is invalid
(search google for type-based-alias-analysis for tons of info).
 
J

junky_fellow

No, the aliasing rules (6.5/7 in the standard) say this is invalid
(search google for type-based-alias-analysis for tons of info).

Is anyone aware of some implementation on which,
a pointer to an object when converted to a pointer to object whose
type requires less or equally strict storage alignment, may cause
undefined behaviour ?

I think such a conversion should be legal even if the
two pointers have entirely different representation.
For instance,
take an example of malloc() function. The malloc() function is
guaranteed to return memory that is suitably aligned for any type.
(ie it should be returning the memory which is aligned to the object
that requires most strict storage alignment). Afterwards this
may be converted to any pointer type even if it has a completely
different representation. I think the conversion function for that
implementation takes care of that. So, it seems that there
should be some kind of relation between the representation of the
pointer to object that has most strict storage alignment and any
other pointer type (otherwise the conversion will not be possible).

Similarly, the conversion of pointer type with more strict storage
alignment to the pointer type with less or equally strict alignment
should be legal. Although standard doesn't say anything about this,
but I don't find any reason why it will not be.
Is anyone aware of any such implementation where such a conversion
is not valid ?
 
E

Eric Sosman

Is anyone aware of some implementation on which,
a pointer to an object when converted to a pointer to object whose
type requires less or equally strict storage alignment, may cause
undefined behaviour ?

Yes, and I've been bitten by it. The context was a
typical "poor man's O-O" scheme. Every struct of interest
had a type descriptor as its first element, so the code
could determine the type of an "anonymous" struct pointer
and decide what cast to apply. Vastly simplified:

struct common {
enum { TYPE1, TYPE2 } type;
... other universal goodies ...
};

struct type1 {
struct common prefix;
... other type1 goodies ...
};

struct type2 {
struct common prefix;
... a *lot* of type2 goodies ...
};

It turned out to be important (and deadly) that struct type2
was much larger than struct type1.

One function was supposed to do something special to a
struct type2, but was a no-op for all other types. It was
written like this:

void func2(struct type2 *ptr) {
/* check for proper type first! */
if (ptr->prefix.type == TYPE2) {
/* safe to access type2-specific fields */
... do the type2 magic ...
}
}

Can you spot the error? (Hint: "If you lie to the compiler,
it will get its revenge." Where's the lie?)

spoiler space


x


x


x


x


x


x


x


x


x


x


The lie is in the argument declaration: it says the
function recieves a pointer to a struct type2, but that
isn't actually known until after the test. An optimizing
compiler decided to issue pre-fetch instructions for a few
of the type2-specific fields before testing the type code,
presumably to optimize the use of a hardware cache. This
was harmless most of the time -- the machine fetched a few
data items and then decided not to use them. But one day
the function encountered a pointer to a (small) struct type1
that just happened to live at the very end of a region of
mapped memory, with non-existent addresses following it.
Pre-fetching fields from the (larger) struct type2 wound
up trying to reference the non-existent memory, provoking
a visitation by the nasal demon named SIGSEGV.

The fix was to make the function honest by rewriting
it slightly:

void func2(struct common *pfxptr) {
/* check for proper type first! */
if (pfxptr->type == TYPE2) {
/* we have a bona-fide type2 pointer */
struct type2 *ptr = (struct type2*)pfxptr;
/* safe to access type2-specific fields */
... do the type2 magic ...
}
}

Now, you may be wondering what all this has to do with
alignment, and the answer is "Not much." The point is that
you can get undefined behavior by trying to access an object
of one type through a pointer to some other type, or even if
you *don't* access the object. The U.B. doesn't need alignment
problems to work its evil will; the original lie is sufficient
all by itself.

(Because the crash occurred only when a struct type1 just
happened to occupy a particular memory location, it was awfully
hard to reproduce; I'd never have solved the problem without
the very hard and patient work of a Q/A engineer. Even then
I might not have figured out the mechanism without the help of
a fellow programmer who happened to have spent many years as a
compiler developer and was attuned to the tricks of optimizers.
This was a Little White Lie that turned into a Very Expensive
Nightmare, tying up three engineers for the better part of a
week; the experience has taught me to "go the extra mile" in
trying to write squeaky-clean code. Perhaps you will learn
the lesson without first incurring the pain ...)
 
C

CBFalconer

Eric said:
Yes, and I've been bitten by it. The context was a
typical "poor man's O-O" scheme. Every struct of interest
had a type descriptor as its first element, so the code
could determine the type of an "anonymous" struct pointer
and decide what cast to apply. Vastly simplified:

struct common {
enum { TYPE1, TYPE2 } type;
... other universal goodies ...
};

struct type1 {
struct common prefix;
... other type1 goodies ...
};

struct type2 {
struct common prefix;
... a *lot* of type2 goodies ...
};

It turned out to be important (and deadly) that struct type2
was much larger than struct type1.

One function was supposed to do something special to a
struct type2, but was a no-op for all other types. It was
written like this:

void func2(struct type2 *ptr) {
/* check for proper type first! */
if (ptr->prefix.type == TYPE2) {
/* safe to access type2-specific fields */
... do the type2 magic ...
}
}

Can you spot the error? (Hint: "If you lie to the compiler,
it will get its revenge." Where's the lie?)

Leaving the solution for others to review in your original,
wouldn't the following have done nicely, by relying on the
guarantee that the first item in the structure lies at the address
of the structure.

void func2(void *ptr) {
struct common *id = ptr;
struct type2 *p;

if (id->type == TYPE2) {
p = ptr;
/* access away through p */
}
}

the penalty being that it abandons type checking on func2 calls.
 
E

Eric Sosman

CBFalconer said:
Eric said:
[... see up-thread for the preliminaries, if interested ...]
One function was supposed to do something special to a
struct type2, but was a no-op for all other types. It was
written like this:

void func2(struct type2 *ptr) {
/* check for proper type first! */
if (ptr->prefix.type == TYPE2) {
/* safe to access type2-specific fields */
... do the type2 magic ...
}
}

Can you spot the error? (Hint: "If you lie to the compiler,
it will get its revenge." Where's the lie?)


Leaving the solution for others to review in your original,
wouldn't the following have done nicely, by relying on the
guarantee that the first item in the structure lies at the address
of the structure.

void func2(void *ptr) {
struct common *id = ptr;
struct type2 *p;

if (id->type == TYPE2) {
p = ptr;
/* access away through p */
}
}

the penalty being that it abandons type checking on func2 calls.

Yes, that would have worked equally well.

I actually lied a bit in my example code: not only did I
simplify it a lot, but I also updated it from pre-Standard C
where we didn't have `void' and `void*'. The original actually
looked more like

int func2(ptr)
struct type2 *ptr;
{...}

I decided to update it because some posters to comp.lang.c have
revealed that they are not aware of the pre-Standard syntax for
function definitions. Personally, I regard this as a Good Thing
and a testimony to the attractiveness of prototypes.
 
L

Lawrence Kirby

On Tue, 07 Jun 2005 18:33:07 -0400, Eric Sosman wrote:

....
I actually lied a bit in my example code: not only did I
simplify it a lot, but I also updated it from pre-Standard C
where we didn't have `void' and `void*'. The original actually
looked more like

int func2(ptr)
struct type2 *ptr;
{...}


Were you using a pre-standard compiler? If so that's a pretty subtle
optimisation the compiler was making for a language not so formally
defined. I wonder if it compiled other "correct" variants properly.

Lawrence
 
E

Eric Sosman

Lawrence said:
On Tue, 07 Jun 2005 18:33:07 -0400, Eric Sosman wrote:

...





Were you using a pre-standard compiler? If so that's a pretty subtle
optimisation the compiler was making for a language not so formally
defined. I wonder if it compiled other "correct" variants properly.

The compiler was "post-Standard" (which sounds oxymoronic,
like "post-modern"), but the code presented to it was of older
vintage. Some of us agitated for upgrading the code, but some
of the more senior folks nixed the effort: "All that work for
no benefit," they said. 'Course, the bug that cost me all that
time and effort would have been caught by the compiler if the
function had been modernly and properly prototyped ...
 

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,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top