Struct wrapper conversion

L

Lauri Alanko

Given:

typedef struct { int foo; } Bar;

The following is legal (N869 6.7.2.1#12):

Bar b = { 42 };
int* ip = &bar.foo;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

But this, to my understanding, might not be:

int i = 42;
int* ip = &i;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

The only practical problem I can think of here is that Bar and int
might have different alignments for some reason.

However, all structs are guaranteed to have the same alignment
(6.2.5#27), so how about the following?

typedef struct { Bar bar; } Baz;

Bar b = { 42 };
Baz* zp = (Baz*) &b;
assert(zp->bar.foo == 42);

At this point, although I'm guessing this isn't strictly conforming, I
can't think of any real-world reason why this wouldn't work.

Anyone?


Lauri
 
J

James Kuyper

Given:

typedef struct { int foo; } Bar;

The following is legal (N869 6.7.2.1#12):

Bar b = { 42 };
int* ip = &bar.foo;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

But this, to my understanding, might not be:

int i = 42;
int* ip = &i;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

The only practical problem I can think of here is that Bar and int
might have different alignments for some reason.

There's other possibilities, as well. For instance, the natural word
size for a given machine might be too big to be convenient to use as
sizeof(int), but the implementation might choose to pad struct Bar to
the word size. That would allow the use of word-size instructions to
read or write bp->foo, with padding bits incidentally being affected by
such operations. If ip+1 happens to point at some piece of memory that
is already in use for some other reason, or is perhaps simply
inaccessible to your program, this would cause obvious problems.
However, all structs are guaranteed to have the same alignment
(6.2.5#27), so how about the following?

6.2.5p27 says that all pointers to structs have the same representation
and alignment requirements; the structs that they point to could have
quite different alignment requirements.
 
S

Shao Miller

Given:

typedef struct { int foo; } Bar;

The following is legal (N869 6.7.2.1#12):

Bar b = { 42 };
int* ip =&bar.foo;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

But this, to my understanding, might not be:

int i = 42;
int* ip =&i;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

The only practical problem I can think of here is that Bar and int
might have different alignments for some reason.

Agreed. They might have different alignment requirements. The
alignment of 'int' would have to be satisfied by the alignment of 'Bar',
but not vice versa.
However, all structs are guaranteed to have the same alignment
(6.2.5#27), so how about the following?

As Mr. James Kuyper already pointed out, that point is in regards to
pointers. Thus a conversion of a 'struct XXX *' to a 'struct YYY *' is
defined regardless of 'XXX' and 'YYY'; the alignment of the pointer
types is good[6.3.2.3p7]. Same with unions.
typedef struct { Bar bar; } Baz;

Bar b = { 42 };
Baz* zp = (Baz*)&b;
assert(zp->bar.foo == 42);

At this point, although I'm guessing this isn't strictly conforming, I
can't think of any real-world reason why this wouldn't work.

Anyone?

Same problem. The alignment of 'Bar' would have to be satisfied by the
alignment of 'Baz', but not vice versa. If a 'Bar' could only be at
every 4 bytes and a 'Baz' could only be at every 12 bytes, 'b' might be
at 8 bytes. An implementation might notice such an attempt even at
translation-time, since we can discuss it here by looking at it. :)
 
B

Barry Schwarz

Given:

typedef struct { int foo; } Bar;

The following is legal (N869 6.7.2.1#12):

Bar b = { 42 };
int* ip = &bar.foo;

This is not legal since there is no object named bar to take the
address of. Did you mean &b.foo?
Bar* bp = (Bar*) ip;

This is legal only becuase the value in ip is the address of the first
element of a Bar which is guaranteed to be the address of the Bar
itself and is therefore properly aligned for a Bar*.
assert(bp->foo == 42);

But this, to my understanding, might not be:

int i = 42;
int* ip = &i;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

The only practical problem I can think of here is that Bar and int
might have different alignments for some reason.

However, all structs are guaranteed to have the same alignment
(6.2.5#27), so how about the following?

typedef struct { Bar bar; } Baz;

Bar b = { 42 };
Baz* zp = (Baz*) &b;

This has the same problem. There is no guarantee that a Baz does not
have a more restrictive alignment than a Bar. If it does, then the
assignment to zp could invoke undefined behavior.
 
O

Old Wolf

int i = 42;
int* ip = &i;
Bar* bp = (Bar*) ip;
assert(bp->foo == 42);

The only practical problem I can think of here is that Bar and int
might have different alignments for some reason.

Another problem might be if Bar has padding after
foo, then bounds-checking on 'bp' might indicate a
pointer pointing to a unit that goes past the end
of allocated memory
 

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,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top