![]() |
Re: Compatible structs
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 esosman@ieee-dot-org.invalid |
Re: Compatible structs
"Eric Sosman" <esosman@ieee-dot-org.invalid> wrote in message
news:iaouhn$cqi$1@news.eternal-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 |
Re: Compatible structs
Eric Sosman <esosman@ieee-dot-org.invalid> 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. |
Re: Compatible structs
On 11/2/2010 8:55 AM, Vrtt wrote:
> "Eric Sosman"<esosman@ieee-dot-org.invalid> wrote in message > news:iaouhn$cqi$1@news.eternal-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 esosman@ieee-dot-org.invalid |
Re: Compatible structs
"christian.bau" <christian.bau@cbau.wanadoo.co.uk> wrote in message news:34fa8180-b3f8-47ee-988d-46839ed33ecf@26g2000yqv.googlegroups.com... > On Nov 2, 12:37 pm, "BartC" <b...@freeuk.com> 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 |
Re: Compatible structs
On Nov 3, 5:03*am, "BartC" <b...@freeuk.com> wrote:
> "christian.bau" <christian....@cbau.wanadoo.co.uk> wrote in message > > news:34fa8180-b3f8-47ee-988d-46839ed33ecf@26g2000yqv.googlegroups.com... > > > On Nov 2, 12:37 pm, "BartC" <b...@freeuk.com> 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? |
Re: Compatible structs
"crisgoogle" <crisgoogle@telus.net> wrote in message news:f41f8d54-f326-4821-8207-04f7ebcd3c08@n32g2000pre.googlegroups.com... > On Nov 3, 5:03 am, "BartC" <b...@freeuk.com> 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 |
Re: Compatible structs
crisgoogle <crisgoogle@telus.net> 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] |
Re: Compatible structs
On Nov 3, 2:18*pm, "BartC" <b...@freeuk.com> wrote:
> "crisgoogle" <crisgoo...@telus.net> wrote in message > > news:f41f8d54-f326-4821-8207-04f7ebcd3c08@n32g2000pre.googlegroups.com... > > > On Nov 3, 5:03 am, "BartC" <b...@freeuk.com> 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. |
Re: Compatible structs
On 2010-11-03, Mark Wooding <mdw@distorted.org.uk> 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 / usenet-nospam@seebs.net 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. |
| All times are GMT. The time now is 06:54 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.