/* ** $Id: inherit.c 802 2008-03-01 19:26:43Z phf $ ** ** Strange way to simulate "inheritance" of data fields. We rely ** on the fact that nested structs in fact get laid out one after ** the other in memory. ** ** This is *not* guaranteed to work on all compilers and at all ** optimization levels (although it does seem to work for GCC ** and even at -O3). ** ** I have seen this used a lot since I grew up with Commodare ** Amiga machines. AmigaOS used this kind of "inheritance" a ** lot, even in the exec.library which was the "micro kernel" ** of the thing. Pretty cool: object-oriented stuff in a 1984 ** design. :-) */ #include #include #include // a "base class" with a few fields struct base { double numeric; char bla; int something; int array[2]; }; // a "derived class" that contains the "base class" as its // first field and then adds a few more struct extend { struct base base; float aha; char whatever; double good[128]; }; // a further "derived class" just to demo that this works // for multiple levels of "inheritance" :-) struct further { struct extend base; char name[80]; }; int main(void) { struct base b; struct extend e; struct further f; // We start by measuring some offsets. There is actually // a macro called "offsetof" in the C library, but it's // a little cryptic (look up how it is defined, might be // on the midterm :-). printf("offsetof(struct extend, whatever)==%zu\n", offsetof(struct extend, whatever)); // On my Mac laptop, the offset of the field is 28, but // your milage may vary. We can now explore where the // fields of the "base class" end up in the "derived // class". For example, let's find the offset for the // field "something" in struct base: printf("offsetof(struct base, something)==%zu\n", offsetof(struct base, something)); // On my Mac laptop, the offset is 12. Where is that // field in the derived class? We find the same field // as "base.something" since we nested a struct base // into the struct extend. So let's see where "base" // ends up, and then where "base.something" is. printf("offsetof(struct extend, base)==%zu\n", offsetof(struct extend, base)); printf("offsetof(struct extend, base.something)==%zu\n", offsetof(struct extend, base.something)); // On my Mac laptop, the offset for "base" is 0 while // the offset for "base.something" is 12 again, just // like the offset of "something" within struct base. // I think you see where this is going now: Since the // compiler hands out the same offsets in both cases, // we can "trick it" into treating a struct extend as // a struct base by casting. Sadly enough, it does not // work without pointers (why?): // b = (struct base) e; // However, if we use pointers, we can perform the cast // and then access the "base part" of a struct extend // the same way we would access it in a struct base: struct base *bp = &b; struct extend *ep = &e; struct further *fp = &f; // initialize the "something" field in both b and e bp->something = 10; printf("bp->something: %d\n", bp->something); ep->base.something = 20; printf("ep->base.something: %d\n", ep->base.something); // cast the "extended" pointer to a "base" pointer bp = (struct base*) ep; // access the fields in e as if they were in b! printf("bp->something: %d\n", bp->something); bp->something += 1; printf("bp->something: %d\n", bp->something); // of course, the assignment to "something" affects e... printf("ep->base.something: %d\n", ep->base.something); // one more level of "inheritance" just for fun fp->base.base.something = 30; printf("fp->base.base.something: %d\n", fp->base.base.something); bp = (struct base*) fp; printf("bp->something: %d\n", bp->something); // So by nesting structs this way, we can simulate what // happens in Java when you have a "base class" with a // few data fields and a "derived class" with a few more: // You can still access the base class fields for an // object of the derived class. Woohoo! :-) Of course // we only have one "piece" of true OO now, but this is // already useful. return EXIT_SUCCESS; }