Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Re: Compatible structs

Reply
Thread Tools

Re: Compatible structs

 
 
Eric Sosman
Guest
Posts: n/a
 
      11-02-2010
On 11/2/2010 7:12 AM, Vrtt wrote:
> Hi all,
>
> I couldn't find this question in the FAQ, although it looks like a
> frequently asked one. Apologies if it's been answered here before.
>
> I've seen this idiom a lot in libraries:-
>
> struct base {
> int x;
> char y;
> };
>
> struct extended {
> int x;
> char y;
> double z;
> };
>
> void base_func(struct base *b);
>
> struct extended e;
> base_func((struct base*)&e);
>
> Is this actually guaranteed to work, or can struct alignment (or something
> else) mess it up?


It will almost certainly work as expected, but it is not
actually guaranteed to work. If there were a union containing
both struct types it and if `e' resided in a union instance:

union { struct base b; struct extended e; } u;
base_func((struct base*)&u.e);
/* or even ... */ base_func(&u.b);

.... would be guaranteed to work. Absent the enclosing union,
C is in theory free to arrange the structs' first two elements
differently -- but since C supports separate compilation, the
compiler must usually assume that such a union might exist in
some other module, and since all `struct extended' must look
the same throughout the entire program, it most likely arranges
things as you'd expect.

However, the layout of the structs is not the only issue.
I've encountered actual trouble with actual real compilers in
handling constructs closely related to this one, and my advice
would be to shun the practice whenever possible. In the case
at hand it's pretty easy to avoid the problem altogether:

struct extended {
struct base b;
double z;
};
...
struct extended e;
base_func (&e.b);

.... is pure as the driven snow, 100% safe, and highly recommended.
Yes, you now must write `e.b.x' instead of `e.x', but that's not
usually a serious hardship in actual use.

--
Eric Sosman
http://www.velocityreviews.com/forums/(E-Mail Removed)lid
 
Reply With Quote
 
 
 
 
BartC
Guest
Posts: n/a
 
      11-02-2010
"Eric Sosman" <(E-Mail Removed)> wrote in message
news:iaouhn$cqi$(E-Mail Removed)-september.org...
> On 11/2/2010 7:12 AM, Vrtt wrote:
>> Hi all,
>>
>> I couldn't find this question in the FAQ, although it looks like a
>> frequently asked one. Apologies if it's been answered here before.
>>
>> I've seen this idiom a lot in libraries:-
>>
>> struct base {
>> int x;
>> char y;
>> };
>>
>> struct extended {
>> int x;
>> char y;
>> double z;
>> };
>>
>> void base_func(struct base *b);
>>
>> struct extended e;
>> base_func((struct base*)&e);
>>
>> Is this actually guaranteed to work, or can struct alignment (or
>> something
>> else) mess it up?

>
> It will almost certainly work as expected, but it is not
> actually guaranteed to work. If there were a union containing
> both struct types it and if `e' resided in a union instance:
>
> union { struct base b; struct extended e; } u;
> base_func((struct base*)&u.e);
> /* or even ... */ base_func(&u.b);
>
> ... would be guaranteed to work. Absent the enclosing union,
> C is in theory free to arrange the structs' first two elements
> differently


Why should the union make a difference?

One access to the .y field might be u.b.y, and another might be u.e.y; why
do the offsets of the two .y fields need to be guaranteed the same?

I can't see that it's any different to having a base struct like this:

struct base {
char y;
int x;
};

There's no reason to reverse the fields to match the extended struct, even
if they are both in the same union.

--
Bartc

 
Reply With Quote
 
 
 
 
Tim Rentsch
Guest
Posts: n/a
 
      11-02-2010
Eric Sosman <(E-Mail Removed)> writes:

> On 11/2/2010 7:12 AM, Vrtt wrote:
>> Hi all,
>>
>> I couldn't find this question in the FAQ, although it looks like a
>> frequently asked one. Apologies if it's been answered here before.
>>
>> I've seen this idiom a lot in libraries:-
>>
>> struct base {
>> int x;
>> char y;
>> };
>>
>> struct extended {
>> int x;
>> char y;
>> double z;
>> };
>>
>> void base_func(struct base *b);
>>
>> struct extended e;
>> base_func((struct base*)&e);
>>
>> Is this actually guaranteed to work, or can struct alignment (or something
>> else) mess it up?

>
> It will almost certainly work as expected, but it is not
> actually guaranteed to work. If there were a union containing
> both struct types it and if `e' resided in a union instance:
>
> union { struct base b; struct extended e; } u;
> base_func((struct base*)&u.e);
> /* or even ... */ base_func(&u.b);
>
> ... would be guaranteed to work. [snip]


Not quite. To ensure defined-ness, the union definition
needs to be visible at the point where the accesses are
done, ie, in base_func. Unless base_func is defined
later on in the same file as the above call, this code is
still undefined behavior.
 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      11-03-2010
On 11/2/2010 8:55 AM, Vrtt wrote:
> "Eric Sosman"<(E-Mail Removed)> wrote in message
> news:iaouhn$cqi$(E-Mail Removed)-september.org...
>>
>> struct extended {
>> struct base b;
>> double z;
>> };
>> ...
>> struct extended e;
>> base_func (&e.b);
>>
>> ... is pure as the driven snow, 100% safe, and highly recommended.
>> Yes, you now must write `e.b.x' instead of `e.x', but that's not
>> usually a serious hardship in actual use.
>>

>
> Could I not reliably do:
>
> base_func((void*)&e);
>
> ...in this case?


Yes, but only because your base_func() has a prototype calling
for a `struct base*' argument. The call as written begins with a
`struct extended*', converts that to a `void*' by means of a cast,
and then converts the `void*' to a `struct base*' by virtue of the
prototype. You could equally well have written

base_func((struct base*)&e);

.... to avoid the intermediate conversion.

I'd suggest, though, that adding a conversion where a conversion-
free alternative exists is a step in the wrong direction. Theorem:
When you write a cast to "get the compiler to do the right thing,"
you are probably asking it to do something wrong. In the present case
you've got a function that takes a `struct base*' and you've got a
`struct base' instance ready to hand: just point at it and be done.
Costuming the `struct base*' as a `struct extended*' and then as a
`void*' and then as a `struct base*' again is just an indication that
you're too enthusiastic about playing dress-up, even after Halloween
is past.

--
Eric Sosman
(E-Mail Removed)lid
 
Reply With Quote
 
BartC
Guest
Posts: n/a
 
      11-03-2010


"christian.bau" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> On Nov 2, 12:37 pm, "BartC" <(E-Mail Removed)> wrote:
>
>> Why should the union make a difference?

>
> Because the C Standard says so.
>
> If you have two structs s1 and s2, and there is a union u containing
> both struct s1 and struct s2, then the compiler must assume that any
> pointer to a struct s1 is actually a pointer to an element of a union
> u, and any pointer to a struct s2 might also be a pointer to an
> element of the same union, and it must produce code that is correct in
> that case. And the reason why the compiler has to assume this is
> because the C Standard says so.


Well I still don't get it (why the offsets of compatible fields in two
struct types must match, when they belong in the same union, but not
otherwise).

And I can think of times when you don't want that behaviour (the two structs
might have different alignments of the fields, even if the structs are in
the same union).

--
Bartc

 
Reply With Quote
 
crisgoogle
Guest
Posts: n/a
 
      11-03-2010
On Nov 3, 5:03*am, "BartC" <(E-Mail Removed)> wrote:
> "christian.bau" <(E-Mail Removed)> wrote in message
>
> news:(E-Mail Removed)...
>
> > On Nov 2, 12:37 pm, "BartC" <(E-Mail Removed)> wrote:

>
> >> Why should the union make a difference?

>
> > Because the C Standard says so.

>
> > If you have two structs s1 and s2, and there is a union u containing
> > both struct s1 and struct s2, then the compiler must assume that any
> > pointer to a struct s1 is actually a pointer to an element of a union
> > u, and any pointer to a struct s2 might also be a pointer to an
> > element of the same union, and it must produce code that is correct in
> > that case. And the reason why the compiler has to assume this is
> > because the C Standard says so.

>
> Well I still don't get it (why the offsets of compatible fields in two
> struct types must match, when they belong in the same union, but not
> otherwise).


And I still don't get exactly what your question is. Does the
matching, or
non-matching behaviour bother you?

Given C's inclination not to define things any more strictly than
necessary, allowing otherwise-identical-looking structs to have
different
internal alignments makes sense.

However, forcing the alignment to be the same under special
circumstances,
thereby allowing aliasing of different struct types, seems useful.

> And I can think of times when you don't want that behaviour (the two structs
> might have different alignments of the fields, even if the structs are in
> the same union).


And I can't possibly imagine a scenario where you specifically want to
prevent having compatible alignment. Or was that not what you're
getting at
here?

 
Reply With Quote
 
BartC
Guest
Posts: n/a
 
      11-03-2010


"crisgoogle" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> On Nov 3, 5:03 am, "BartC" <(E-Mail Removed)> wrote:


>> Well I still don't get it (why the offsets of compatible fields in two
>> struct types must match, when they belong in the same union, but not
>> otherwise).

>
> And I still don't get exactly what your question is. Does the
> matching, or
> non-matching behaviour bother you?


It's why the standard specifically says they must match when part of a
union. What's so special about a union?

> Given C's inclination not to define things any more strictly than
> necessary, allowing otherwise-identical-looking structs to have
> different
> internal alignments makes sense.


OK, sometimes it makes sense to align the fields the same way, sometimes it
doesn't. But according to this thread, using union will guarantee the
alignment. I just wondered what is was about an union that made it important
to align fields (eg. see the field .a in the example below).

> However, forcing the alignment to be the same under special
> circumstances,
> thereby allowing aliasing of different struct types, seems useful.
>
>> And I can think of times when you don't want that behaviour (the two
>> structs
>> might have different alignments of the fields, even if the structs are in
>> the same union).

>
> And I can't possibly imagine a scenario where you specifically want to
> prevent having compatible alignment. Or was that not what you're
> getting at
> here?


Well, yes. For example:

struct {char c; int a;} s1; /* 8 bytes with pack(4) */
struct {char c; int a;} s2; /* 5 bytes with pack(1) */

You might want s1 for efficiency, or compatibility with external software.
And you might want s2 for the same sorts of reasons (but space vs. speed)

Whatever the reasons, you might well want a union of these two structs, but
where s1.a has offset 4, and s2.a has offset 1.

--
Bartc

 
Reply With Quote
 
Mark Wooding
Guest
Posts: n/a
 
      11-03-2010
crisgoogle <(E-Mail Removed)> writes:

> Given C's inclination not to define things any more strictly than
> necessary, allowing otherwise-identical-looking structs to have
> different internal alignments makes sense.
>
> However, forcing the alignment to be the same under special
> circumstances, thereby allowing aliasing of different struct types,
> seems useful.


The puzzling thing is that this leaves implementations with exceedingly
little leeway.

Let's put some stuff in header files to save typing:

foo.h:
struct foo {
int x;
char y;
double z;
struct foo *a;
};

extern void print_foo(const struct foo *f);

bar.h:
struct bar {
int x;
char y;
double z;
unsigned long a;
struct bar *b;
};

extern void print_bar(const struct bar *b);

Now for some actual source files.

foo.c:
#include <stdio.h>
#include "foo.h"

void print_foo(const struct foo *f)
{ printf("x = %d, y = %c, z = %g\n", f->x, f->y, f->z); }

bar.c:
#include <stdio.h>
#include "bar.h"

void print_bar(const struct bar *b)
{
printf("x = %d, y = %c, z = %g, a = %lu\n",
b->x, b->y, b->z, b->a);
}

So we feed these two source files to our typical compiler, and it
produces object files. Was the compiler allowed to make the structures
incompatible?

I say `no'.

splat.c:
#include <stdio.h>
#include <string.h>
#include "foo.h"
#include "bar.h"

union splat {
struct foo f;
struct bar b;
};

static void populate_foo(struct foo *f)
{ f->x = 4; f->y = 'q'; f->z = 3.141; f->a = &f; }

static void populate_bar(struct bar *b)
{
b->x = 5; b->y = 'z'; b->z = 2.183;
b->a = 0xdeadbeef; b->b = &b;
}

int main(int argc, char *argv[])
{
/* see below */

return (0);
}

There are several interesting things we might put in `main'. We might
say this, for example:

in `main':
union splat s;

populate_bar(&s.b);
print_foo(&s.f);

Of course, `print_foo' doesn't know anything about the union (barring
linker connivance that it's really hard to see provides any benefit to
anyone) because it's in a different translation unit. But it has to
work anyway.

It's not just because the structure is part of a union.

in `main' (alternate 2):
union splat s;
struct foo f;
struct bar b;

populate_bar(&b);
memcpy(&s.b, &b, sizeof(b));
memcpy(&f, &s.f, sizeof(f));
/* careful now: f.a is indeterminate */
print_foo(&f); /* doesn't touch f.a */

Now we wonder what good the union actually does here. There's no
address magic, because a union is at the same address as all of its
members. All of the bits were hauled about as unsigned chars (courtesy
of memcpy). Why would the above be different from this?

in `main' (alternate 3):
struct foo f;
struct bar b;

populate_bar(&b);
memcpy(&f, &b, sizeof(b) < sizeof(f) ? sizeof(b) : sizeof(f));
/* careful now: f.a is indeterminate */
print_foo(&f); /* doesn't touch f.a */

For extra fun, why does the union declaration have to be in /this/
translation unit? We can get more amusing action-at-a-distance by
putting it somewhere else.

So, is there actually some valuable latitude here? Was there intended
to be any latitude in laying out common prefixes of structures?

-- [mdw]
 
Reply With Quote
 
crisgoogle
Guest
Posts: n/a
 
      11-03-2010
On Nov 3, 2:18*pm, "BartC" <(E-Mail Removed)> wrote:
> "crisgoogle" <(E-Mail Removed)> wrote in message
>
> news:(E-Mail Removed)...
>
> > On Nov 3, 5:03 am, "BartC" <(E-Mail Removed)> wrote:
> >> Well I still don't get it (why the offsets of compatible fields in two
> >> struct types must match, when they belong in the same union, but not
> >> otherwise).

>
> > And I still don't get exactly what your question is. Does the
> > matching, or
> > non-matching behaviour bother you?

>
> It's why the standard specifically says they must match when part of a
> union. What's so special about a union?


Well, according to the standard itself, it's "to simplify the use of
unions". It allows you to overlay different struct types in a union,
but access common initial members safely. The alternative would be
to force you to use a union of struct types _each_ of which shared
the same "base" struct. This would add a layer that isn't needed
when the union rule is in place.

> > Given C's inclination not to define things any more strictly than
> > necessary, allowing otherwise-identical-looking structs to have
> > different
> > internal alignments makes sense.

>
> OK, sometimes it makes sense to align the fields the same way, sometimes it
> doesn't. But according to this thread, using union will guarantee the
> alignment. I just wondered what is was about an union that made it important
> to align fields (eg. see the field .a in the example below).
>
> > However, forcing the alignment to be the same under special
> > circumstances,
> > thereby allowing aliasing of different struct types, seems useful.

>
> >> And I can think of times when you don't want that behaviour (the two
> >> structs
> >> might have different alignments of the fields, even if the structs are in
> >> the same union).

>
> > And I can't possibly imagine a scenario where you specifically want to
> > prevent having compatible alignment. Or was that not what you're
> > getting at
> > here?

>
> Well, yes. For example:
>
> struct {char c; int a;} s1; * * /* 8 bytes with pack(4) */
> struct {char c; int a;} s2; * * /* 5 bytes with pack(1) */


The obvious response here is that any way to affect the packing
is non-standard right off the bat, so who cares what the standard
has to say about unions of these things?

> You might want s1 for efficiency, or compatibility with external software..
> And you might want s2 for the same sorts of reasons (but space vs. speed)


But if you have a union of the things, the total size has to be the
same
regardless of the alignments of the individual bits. So there is no
savings
in size -- there _may_ be savings in speed due to alignment I suppose.

> Whatever the reasons, you might well want a union of these two structs, but
> where s1.a has offset 4, and s2.a has offset 1.


And if the compiler wants to add this non-standard behaviour on top
of other non-standard behaviour (the packing) then it's free to do so.

Now that I think of it, you can even bypass the union rule like so:

struct {char c; int a;} s1; /* 8 bytes with pack(4) */
struct {char c; int a;} s2; /* 5 bytes with pack(1) */

struct { struct s1; } s3;
struct { struct s2; } s4;

union u1 { struct s3; struct s4; };

I _think_ that this union does not meet the "common initial sequence"
criterion, so the two base structs, s1 and s2, can maintain
their different alignment as you require.
 
Reply With Quote
 
Seebs
Guest
Posts: n/a
 
      11-03-2010
On 2010-11-03, Mark Wooding <(E-Mail Removed)> wrote:
> So, is there actually some valuable latitude here? Was there intended
> to be any latitude in laying out common prefixes of structures?


Not really. The key is that you're allowed to optimize as though there
is no way for a reference through one type to affect something of another
type unless the aliasing rules would allow it.

-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / (E-Mail Removed)
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Packed structs vs. unpacked structs: what's the difference? Daniel Rudy C Programming 15 04-10-2006 08:10 AM
Array of structs instead of an array with pointers to structs? Paminu C Programming 5 10-11-2005 07:18 PM
length of an array in a struct in an array of structs in a struct in an array of structs Tuan Bui Perl Misc 14 07-29-2005 02:39 PM
const structs in other structs Chris Hauxwell C Programming 6 04-27-2004 07:03 PM
structs with fields that are structs Patricia Van Hise C Programming 5 04-05-2004 01:37 AM



Advertisments