Velocity Reviews > Questions about pointers to objects and pointers to functions

Questions about pointers to objects and pointers to functions

Marc Thrun
Guest
Posts: n/a

 09-19-2005
Hello,

I've got a few questions:

1) Given the two structs
struct A {
int x;
};

and

struct B {
struct A y;
int z;
};

is it ok to treat a "pointer to an object of type struct B" as a
"pointer to an object of type struct A"?
(I think someone asked something like this some time ago, but
unfortunately I can't find the article anymore)

2) When I now have a function pointer of type
void (*fpa)(struct A *, int);
and a function pointer of type
void (*fpb)(struct B *, int);

and the corresponding functions
void fa(struct A*,int);
and
void fb(struct B*,int);

is it ok to assign a "pointer to fa" to fpb and call the function
through fpb with a "pointer to an object of type struct B" as the first
parameter?

Marc Thrun

SM Ryan
Guest
Posts: n/a

 09-19-2005
Marc Thrun <(E-Mail Removed)> wrote:
# Hello,
#
# I've got a few questions:
#
# 1) Given the two structs
# struct A {
# int x;
# };
#
# and
#
# struct B {
# struct A y;
# int z;
# };
#
# is it ok to treat a "pointer to an object of type struct B" as a
# "pointer to an object of type struct A"?

Given
struct B sample
you are guarenteed
(struct A*)(&sample) == &(sample.y)
that is, a pointer to a struct is also a pointer to the
first field.

--
SM Ryan http://www.rawbw.com/~wyrmwif/

Tim Rentsch
Guest
Posts: n/a

 09-19-2005
Marc Thrun <(E-Mail Removed)> writes:

> Hello,
>
> I've got a few questions:
>
> 1) Given the two structs
> struct A {
> int x;
> };
>
> and
>
> struct B {
> struct A y;
> int z;
> };
>
> is it ok to treat a "pointer to an object of type struct B" as a
> "pointer to an object of type struct A"?
> (I think someone asked something like this some time ago, but
> unfortunately I can't find the article anymore)

This question might mean a couple of different things. If you're
asking about converting (casting) a 'struct B *' to a 'struct A *'
then that has to work, eg

struct B *b = ...;
struct A *a;

a = (struct A*) b;
if( a == &b->y ) /* this 'if' will always be taken */

directly, eg, with one of

memcpy( &a, &b, sizeof a ); /* 1 */

a = * (struct A**) &b; /* 2 */

Either /*1*/ or /*2*/ should also result in a usable pointer that
passes the 'if' test above. That is to say, to the best of my
understanding that is how the language in the Standard should be
understood. It's possible to debate the point; the language used
in talking about such things is not completely clear cut. As a
practical matter, however, it's reasonable to expect that this
result will hold in any actual implementation.

> 2) When I now have a function pointer of type
> void (*fpa)(struct A *, int);
> and a function pointer of type
> void (*fpb)(struct B *, int);
>
> and the corresponding functions
> void fa(struct A*,int);
> and
> void fb(struct B*,int);
>
> is it ok to assign a "pointer to fa" to fpb and call the function
> through fpb with a "pointer to an object of type struct B" as the first
> parameter?

Technically such a call is illegal, since the two types are not
compatible. But, even though it's technically illegal, it's
almost certainly going to work in any actual implementation.

Marc Thrun
Guest
Posts: n/a

 09-19-2005
Tim Rentsch wrote:
> Marc Thrun <(E-Mail Removed)> writes:
>
>
>>Hello,
>>
>>I've got a few questions:
>>
>>1) Given the two structs
>>struct A {
>> int x;
>>};
>>
>>and
>>
>>struct B {
>> struct A y;
>> int z;
>>};
>>
>>is it ok to treat a "pointer to an object of type struct B" as a
>>"pointer to an object of type struct A"?
>>(I think someone asked something like this some time ago, but
>>unfortunately I can't find the article anymore)

>
>
> This question might mean a couple of different things. If you're
> asking about converting (casting) a 'struct B *' to a 'struct A *'
> then that has to work, eg
>

I admit that it's quite a bit unclear, sorry for my not so good english
. But you are right, I meant casting (I just wonder why I did not
take this term actually).
> struct B *b = ...;
> struct A *a;
>
> a = (struct A*) b;
> if( a == &b->y ) /* this 'if' will always be taken */
>
> directly, eg, with one of
>
> memcpy( &a, &b, sizeof a ); /* 1 */
>
> a = * (struct A**) &b; /* 2 */
>
> Either /*1*/ or /*2*/ should also result in a usable pointer that
> passes the 'if' test above. That is to say, to the best of my
> understanding that is how the language in the Standard should be
> understood. It's possible to debate the point; the language used
> in talking about such things is not completely clear cut. As a
> practical matter, however, it's reasonable to expect that this
> result will hold in any actual implementation.
>
>
>
>>2) When I now have a function pointer of type
>>void (*fpa)(struct A *, int);
>>and a function pointer of type
>>void (*fpb)(struct B *, int);
>>
>>and the corresponding functions
>>void fa(struct A*,int);
>>and
>>void fb(struct B*,int);
>>
>>is it ok to assign a "pointer to fa" to fpb and call the function
>>through fpb with a "pointer to an object of type struct B" as the first
>>parameter?

>
>
> Technically such a call is illegal, since the two types are not
> compatible. But, even though it's technically illegal, it's
> almost certainly going to work in any actual implementation.

I thought the same, but was not sure. Assuming I would use a void * for
the first parameter in both functions, and casting it to the type
"struct A*"/"struct B*" should be valid then?

PS: Where can I get a copy of the standard (maybe a draft version)?
Might help me next time .

Tim Rentsch
Guest
Posts: n/a

 09-19-2005
Marc Thrun <(E-Mail Removed)> writes:

> Tim Rentsch wrote:
> > Marc Thrun <(E-Mail Removed)> writes:

[snip]
> >>2) When I now have a function pointer of type
> >>void (*fpa)(struct A *, int);
> >>and a function pointer of type
> >>void (*fpb)(struct B *, int);
> >>
> >>and the corresponding functions
> >>void fa(struct A*,int);
> >>and
> >>void fb(struct B*,int);
> >>
> >>is it ok to assign a "pointer to fa" to fpb and call the function
> >>through fpb with a "pointer to an object of type struct B" as the first
> >>parameter?

> >
> >
> > Technically such a call is illegal, since the two types are not
> > compatible. But, even though it's technically illegal, it's
> > almost certainly going to work in any actual implementation.

>
> I thought the same, but was not sure. Assuming I would use a void * for
> the first parameter in both functions, and casting it to the type
> "struct A*"/"struct B*" should be valid then?

Using

void fa( void *pv, int n ){ ... };
void fb( void *pv, int n ){ ... };
void (*fpa)( void *, int ) = fa;
void (*fpb)( void *, int ) = fb;

makes assignment of the function pointers, and also the resultant
calls, legal. As you point out, it's then necessary to convert the
void* parameters inside the actual function bodies to be of the
appropriate type.

If I were asked, I'd be inclined to recommend using the first approach
(that uses 'struct A*' and 'struct B*' types, and not 'void*' types),
because the stronger type checking done would be more likely to catch
errors than some theoretical advantage that might result from using
'void*'. But that could depend on the local situation and what
tradeoffs were considered important in the context of the particular
project.

> PS: Where can I get a copy of the standard (maybe a draft version)?
> Might help me next time .

I'm sorry, I don't have a URL handy; if you do a google search
I expect you'll find something without too much difficulty.

S.Tobias
Guest
Posts: n/a

 09-20-2005
Tim Rentsch <(E-Mail Removed)> wrote:
> Marc Thrun <(E-Mail Removed)> writes:

>> struct A {
>> int x;
>> };
>>
>> and
>>
>> struct B {
>> struct A y;
>> int z;
>> };

[snip]
>
> a = (struct A*) b;
> if( a == &b->y ) /* this 'if' will always be taken */
>
> directly, eg, with one of
>
> memcpy( &a, &b, sizeof a ); /* 1 */
>
> a = * (struct A**) &b; /* 2 */
>
> Either /*1*/ or /*2*/ should also result in a usable pointer that
> passes the 'if' test above. That is to say, to the best of my
> understanding that is how the language in the Standard should be
> understood.

Generally, I agree, with a warning: /*2*/ is meant to express
reinterpretation (wich is basically what /*1*/ does as well),
but is technically UB (you're accessing the value of `b' with
an incompatible type lvalue), and might cause real trouble in
real world (esp. when compiler optimization is turned on).

--
Stan Tobias
mailx `echo http://www.velocityreviews.com/forums/(E-Mail Removed)LID | sed s/[[:upper:]]//g`

Tim Rentsch
Guest
Posts: n/a

 09-20-2005
"S.Tobias" <(E-Mail Removed)> writes:

> Tim Rentsch <(E-Mail Removed)> wrote:
> > Marc Thrun <(E-Mail Removed)> writes:

>
> >> struct A {
> >> int x;
> >> };
> >>
> >> and
> >>
> >> struct B {
> >> struct A y;
> >> int z;
> >> };

> [snip]
> >
> > a = (struct A*) b;
> > if( a == &b->y ) /* this 'if' will always be taken */
> >
> > directly, eg, with one of
> >
> > memcpy( &a, &b, sizeof a ); /* 1 */
> >
> > a = * (struct A**) &b; /* 2 */
> >
> > Either /*1*/ or /*2*/ should also result in a usable pointer that
> > passes the 'if' test above. That is to say, to the best of my
> > understanding that is how the language in the Standard should be
> > understood.

>
> Generally, I agree, with a warning: /*2*/ is meant to express
> reinterpretation (wich is basically what /*1*/ does as well),
> but is technically UB (you're accessing the value of `b' with
> an incompatible type lvalue), and might cause real trouble in
> real world (esp. when compiler optimization is turned on).

Right, both on the technical UB and on the real potential
for problems. Thank you for pointing this out.

Rather than /*2*/ we might consider /*2'*/:

a = * (struct A *volatile *) &b; /* 2' */

Of course, this access still technically results in UB, but
it's unlikely that the access here will result in any real
world difficulties.

S.Tobias
Guest
Posts: n/a

 09-20-2005
Tim Rentsch <(E-Mail Removed)> wrote:
> "S.Tobias" <(E-Mail Removed)> writes:
>
>> Tim Rentsch <(E-Mail Removed)> wrote:
>> > Marc Thrun <(E-Mail Removed)> writes:

>>
>> >> struct A {
>> >> int x;
>> >> };
>> >>
>> >> and
>> >>
>> >> struct B {
>> >> struct A y;
>> >> int z;
>> >> };

>> [snip]
>> >
>> > a = (struct A*) b;
>> > if( a == &b->y ) /* this 'if' will always be taken */
>> >
>> > directly, eg, with one of
>> >
>> > memcpy( &a, &b, sizeof a ); /* 1 */
>> >
>> > a = * (struct A**) &b; /* 2 */
>> >
>> > Either /*1*/ or /*2*/ should also result in a usable pointer that
>> > passes the 'if' test above. That is to say, to the best of my
>> > understanding that is how the language in the Standard should be
>> > understood.

>>
>> Generally, I agree, with a warning: /*2*/ is meant to express
>> reinterpretation (wich is basically what /*1*/ does as well),
>> but is technically UB (you're accessing the value of `b' with
>> an incompatible type lvalue), and might cause real trouble in
>> real world (esp. when compiler optimization is turned on).

>
> Right, both on the technical UB and on the real potential
> for problems. Thank you for pointing this out.

I think /*1*/ was ok (all pointers to structs have the same
representation), what might cause problems dereferencing it,
but it won't in this case, since A is the first member of B.

> Rather than /*2*/ we might consider /*2'*/:
>
> a = * (struct A *volatile *) &b; /* 2' */
>
> Of course, this access still technically results in UB, but
> it's unlikely that the access here will result in any real
> world difficulties.

Still wrong, look what a compiler might "think":

TYPEA a;
TYPEB b;
//both types are incompatible
b = something_b;
//put something into b
/*...*/
b = something_else_b;
//put something else into b... but wait, let's cache
//it in a register for now, and see what's next
a = *(TYPEA volatile*)&b;
//take the address of b, convert it to ptr to `volatile TYPEA',
//dereference, take value and put into `a'
//lvalue is type `volatile TYPEA'... hmm... nooooo, of course
//it can't mean b here, let's read what that object contains,
//and update b later...
/* a contains something_b */

Perhaps `b' itself should be volatile in this case.

--
Stan Tobias
mailx `echo (E-Mail Removed)LID | sed s/[[:upper:]]//g`

Tim Rentsch
Guest
Posts: n/a

 09-21-2005
"S.Tobias" <(E-Mail Removed)> writes:

> Tim Rentsch <(E-Mail Removed)> wrote:
> > "S.Tobias" <(E-Mail Removed)> writes:
> >
> >> Tim Rentsch <(E-Mail Removed)> wrote:
> >> > Marc Thrun <(E-Mail Removed)> writes:
> >>
> >> >> struct A {
> >> >> int x;
> >> >> };
> >> >>
> >> >> and
> >> >>
> >> >> struct B {
> >> >> struct A y;
> >> >> int z;
> >> >> };
> >> [snip]
> >> >
> >> > a = (struct A*) b;
> >> > if( a == &b->y ) /* this 'if' will always be taken */
> >> >
> >> > You might be asking about converting the object representation
> >> > directly, eg, with one of
> >> >
> >> > memcpy( &a, &b, sizeof a ); /* 1 */
> >> >
> >> > a = * (struct A**) &b; /* 2 */
> >> >
> >> > Either /*1*/ or /*2*/ should also result in a usable pointer that
> >> > passes the 'if' test above. That is to say, to the best of my
> >> > understanding that is how the language in the Standard should be
> >> > understood.
> >>
> >> Generally, I agree, with a warning: /*2*/ is meant to express
> >> reinterpretation (wich is basically what /*1*/ does as well),
> >> but is technically UB (you're accessing the value of `b' with
> >> an incompatible type lvalue), and might cause real trouble in
> >> real world (esp. when compiler optimization is turned on).

> >
> > Right, both on the technical UB and on the real potential
> > for problems. Thank you for pointing this out.

[snip]
> > Rather than /*2*/ we might consider /*2'*/:
> >
> > a = * (struct A *volatile *) &b; /* 2' */
> >
> > Of course, this access still technically results in UB, but
> > it's unlikely that the access here will result in any real
> > world difficulties.

>
> Still wrong, look what a compiler might "think":
>
> TYPEA a;
> TYPEB b;
> //both types are incompatible
> b = something_b;
> //put something into b
> /*...*/
> b = something_else_b;
> //put something else into b... but wait, let's cache
> //it in a register for now, and see what's next
> a = *(TYPEA volatile*)&b;
> //take the address of b, convert it to ptr to `volatile TYPEA',
> //dereference, take value and put into `a'
> //lvalue is type `volatile TYPEA'... hmm... nooooo, of course
> //it can't mean b here, let's read what that object contains,
> //and update b later...
> /* a contains something_b */

It's an interesting argument, but the reasoning is not quite sound.

During code generation, the compiler keeps track (in the data flow
sense) of where the value of 'b' is held at any given moment. When
the address is taken ('&b'), the compiler is going to use the address
of the location where 'b' is currently held. If 'b' is currently held
in a register, and the address value might escape the context that the
compiler can analyze, the register value of 'b' will be written back
to its regular memory location so that the address will point to the
currently meaningful value. Of course, what's going to happen in a
real compiler in this situation is that the compiler will know that
the (casted) address refers to 'b', and the register holding 'b' will
simply be stored into 'a'.

S.Tobias
Guest
Posts: n/a

 09-21-2005
Tim Rentsch <(E-Mail Removed)> wrote:
> "S.Tobias" <(E-Mail Removed)> writes:

>> TYPEA a;
>> TYPEB b;
>> //both types are incompatible
>> b = something_b;
>> //put something into b
>> /*...*/
>> b = something_else_b;
>> //put something else into b... but wait, let's cache
>> //it in a register for now, and see what's next
>> a = *(TYPEA volatile*)&b;
>> //take the address of b, convert it to ptr to `volatile TYPEA',
>> //dereference, take value and put into `a'
>> //lvalue is type `volatile TYPEA'... hmm... nooooo, of course
>> //it can't mean b here, let's read what that object contains,
>> //and update b later...
>> /* a contains something_b */

>
> It's an interesting argument, but the reasoning is not quite sound.
>
> During code generation, the compiler keeps track (in the data flow
> sense) of where the value of 'b' is held at any given moment. When
> the address is taken ('&b'), the compiler is going to use the address
> of the location where 'b' is currently held.

But taking the address of `b' does not automatically mean that
the object `b' is going to be accessed. The decision which object
_may_ be accessed can be based only on the type of lvalue and
effective type of the object, and I believe that this optimization
above is allowed. That's how I imagine aliasing rule works.
But I'd still appreciate others' comments on this.

>If 'b' is currently held
> in a register, and the address value might escape the context that the
> compiler can analyze, the register value of 'b' will be written back
> to its regular memory location so that the address will point to the
> currently meaningful value. Of course, what's going to happen in a
> real compiler in this situation is that the compiler will know that
> the (casted) address refers to 'b', and the register holding 'b' will
> simply be stored into 'a'.

But my point is that the context does *not* escape, it's just that
the compiler determines that `b' cannot be accessed because
the lvalue type doesn't match its effective type.

Since all information is in one expression, it's easy to infer that
in this case object `b' is going to be accessed. However, a compiler
need not be that wise. Have you tried this with DS9000?

--
Stan Tobias
mailx `echo (E-Mail Removed)LID | sed s/[[:upper:]]//g`