Pointer to pointer to struct/first member

I

Ian Pilcher

I know that a pointer to a structure and a pointer to its first member
are largely interchangeable. Is that true for pointers to pointers?

I.e.:

struct foo {
int bar;
};

void int_fn(int **baz)
{
/* do stuff */
}

void foo_fn(struct foo *foo_ptr)
{
int_fn((int **)&foo_ptr); /* Is this cast defined? */

/* do other stuff with foo_ptr */
}

I have a sneaking feeling that it isn't, even though it's likely to work
on just about any mainstream implementation. I supposed that the "safe"
version would be something like:

void foo_fn(struct foo *foo_ptr)
{
int *int_ptr = (int *)foo_ptr;
int_fn(&int_ptr);
foo_ptr = (struct foo *)int_ptr;

/* do other stuff with foo_ptr */
}

Do I have that right?

Thanks!
 
J

James Kuyper

I know that a pointer to a structure and a pointer to its first member
are largely interchangeable. Is that true for pointers to pointers?

Pointers don't have members (at least, none that accessible to strictly
conforming C code), so it can't be true. What you're really asking is
something different.
I.e.:

struct foo {
int bar;
};

void int_fn(int **baz)
{
/* do stuff */
}

void foo_fn(struct foo *foo_ptr)
{
int_fn((int **)&foo_ptr); /* Is this cast defined? */

The only clause that covers that conversion is 6.3.2.3p7: "If the
resulting pointer is not correctly aligned for the referenced type, the
behavior is undefined. Otherwise, when converted back again, the result
shall compare equal to the original pointer." The standard says nothing
about what location (int**)&foo_ptr would point at, nor does it say
anything about what would happen if that pointer were dereferenced. The
behavior is undefined "by the omission of any explicit definition of the
behavior" (4p2). While a struct foo is guaranteed to be correctly
aligned for an int, there's no guarantee that a struct foo* is correctly
aligned for an int*, so you can't even be sure that the conversion is
reversible.

An int* could, in principle, have an entirely different representation,
and even a different size, from a struct foo*. There have been
real-world systems where pointers to types that were required to be
word-aligned merely contained the machine address, whereas pointers to
types with alignment requirements smaller than the word size contained a
machine address for a word, and a byte offset with that word. That's
unlikely to apply in this case, but it's not impossible.

....
I have a sneaking feeling that it isn't, even though it's likely to work
on just about any mainstream implementation. I supposed that the "safe"
version would be something like:

void foo_fn(struct foo *foo_ptr)
{
int *int_ptr = (int *)foo_ptr;

I'd prefer using &foo_ptr.bar. A diagnostic is guaranteed if struct foo
is changed to not contain a field named bar, and it will point to the
correct int if a field is added to struct foo before 'bar'.
 
B

Barry Schwarz

I know that a pointer to a structure and a pointer to its first member
are largely interchangeable. Is that true for pointers to pointers?

It's not what you don't know that hurts, it's what you know that ain't
so.

struct z {int x;} y;
int *px = &y.x;
struct z *py = &y;
scanf("%d", px); /* valid */
scanf("%d", py); /* undefined behavior unless pointer to struct
and pointer to int have the same representation, alignment, etc and
are passed to the called function using the exact same mechanism and
possibly even if these conditions are met */
 
J

James Kuyper

It's not what you don't know that hurts, it's what you know that ain't
so.

struct z {int x;} y;
int *px = &y.x;
struct z *py = &y;
scanf("%d", px); /* valid */
scanf("%d", py); /* undefined behavior unless pointer to struct
and pointer to int have the same representation, alignment, etc and
are passed to the called function using the exact same mechanism and
possibly even if these conditions are met */

I'm assume that what he was referring to was the fact that

scanf("%d", (int*)py);

is valid, and has the same behavior as

scanf("%d", px);
 
I

Ian Pilcher

It's not what you don't know that hurts, it's what you know that ain't
so.

Yeah, that was too strong. I *do* believe that it is defined behavior
to cast a pointer to a structure to a pointer to its first member, and
vice versa.
struct z {int x;} y;
int *px = &y.x;
struct z *py = &y;
scanf("%d", px); /* valid */
scanf("%d", py); /* undefined behavior unless pointer to struct
and pointer to int have the same representation, alignment, etc and
are passed to the called function using the exact same mechanism and
possibly even if these conditions are met */

struct z *pz = (struct z *)px;
scanf("%d", (int *)pz);
 
J

Jorgen Grahn

I know that a pointer to a structure and a pointer to its first member
are largely interchangeable.

Not really -- they have different types and that's a fundamental
difference. That's IMHO a sign that the limited interchangeability
should be exploited rarely and carefully.

/Jorgen
 
S

Siri Cruz

Jorgen Grahn said:
Not really -- they have different types and that's a fundamental
difference. That's IMHO a sign that the limited interchangeability
should be exploited rarely and carefully.

A pointer to a struct and a pointer to the first member point to the same thing
regardless of how the address is represented. This is used to simulate objects
in some case:

struct object {
yadda_t yadda;
};

struct heir {
struct object super;
blah_t blah;
}

struct heir x;
struct object y;
struct object *z;

z = (struct object*)&x;
z = (struct object*)&y;

z->yadda is valid whether z is &x or &y.

It can also be used for pointers to variant structs where the first member is
distinguishing tag.
 
B

Barry Schwarz

Yeah, that was too strong. I *do* believe that it is defined behavior
to cast a pointer to a structure to a pointer to its first member, and
vice versa.


struct z *pz = (struct z *)px;
scanf("%d", (int *)pz);

While pz has type pointer to struct, the expression (int*)pz has type
pointer to int. Replacing the assignment statement with any of the
following

void *pz = px;
char *pz = (char*)px;
struct q *pz = (struct q*)px;

would still allow the call to scanf to function correctly. The point
is that when you use the cast operator, you effectively remove the
relationship between the type pointer to struct and the type pointer
to int.

Your code is taking advantage of the fact that pz, no matter how it is
assigned, points to the same byte that px points to. You are not
taking advantage of any inherent relationship between the types, only
between the values.
 
J

James Kuyper

On 12/18/2013 04:27 PM, Siri Cruz wrote:
....
A pointer to a struct and a pointer to the first member point to the same thing
regardless of how the address is represented. This is used to simulate objects

In the context of C, "object" already has a specific meaning (3.15), and
objects, in that sense, don't need to be simulated - they're built into
the language. In a C-oriented forum, if you use the word with any other
meaning you should mention that fact.

You're using the concept of an "object" from object-oriented
programming, but this feature is neither necessary nor sufficient to
simulate that concept. It would be more accurate to say that this
feature can be used to implement inheritance when writing
object-oriented C code. It's lot easier to use that paradigm in a
language which has built-in support for object orientation, but it can
be done in C.
 

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,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top