36

Suppose I have a number of C structs for which I would like a particular set of functions to operate upon.

I'm wondering if the following is a legitimate approach:

typedef struct Base {
     int exampleMember;
     // ...
} Base;

typedef struct Foo {
     Base base;
     // ...
} Foo;

typedef struct Bar {
     Base base;
     // ...
} Bar;

void MethodOperatesOnBase(void *);

void MethodOperatesOnBase(void * obj)
{
     Base * base = obj;
     base->exampleMember++;
}

In the example you'll notice that both structs Foo and Bar begin with a Base member.

And, that in MethodOperatesOnBase, I cast the void * parameter to Base *.

I'd like to pass pointers to Bar and pointers to Foo to this method and rely on the first member of the struct to be a Base struct.

Is this acceptable, or are there some (possibly compiler-specific) issues I need to be aware of? (Such as some sort of packing/padding scheme that would change the location of the first member of a struct?)

Steve
  • 31,144
  • 19
  • 99
  • 122
  • 2
    Any reason why you don't just use C++? – Jonathan Grynspan Sep 05 '11 at 20:43
  • Yes. But point well taken. I am trying to imitate a super-simple inheritance mechanism for a limited number of structs in my project. – Steve Sep 05 '11 at 20:46
  • Then use C++. It'll make things just as simple, you'll be able to avoid any UB pitfalls, and you'll be able to extend the class hierarchy later with ease if you need to. – Jonathan Grynspan Sep 05 '11 at 20:49
  • 2
    I understand what you're saying, but I can't for this project. – Steve Sep 05 '11 at 20:51
  • Why can't you? Is this homework? Are you working in an environment where C++ is not available? What's the logic here? – Jonathan Grynspan Sep 05 '11 at 20:52
  • 1
    `Is this homework?` Been out of school for 10 years, but thanks. LOL – Steve Sep 05 '11 at 20:53
  • 4
    @Jonathan Grynspan: It's a legitimate question. Suppose your program is 10,000 lines long and written in C. Then you have like about 100 lines of where you need to do this struct-punning. In that case, it's probably worth it to use a bit more effort to keep the entire program compatible in C. – Mysticial Sep 05 '11 at 21:04
  • Or maybe the reason ain't that. :) I didn't say the question was invalid but it helps to know the context for a question like this. – Jonathan Grynspan Sep 05 '11 at 21:06
  • @Mysticial is close to accurate. Except 10,000 lines is an underestimate. Closer to 100,000 - plus peripheral applications with interop libraries (for .NET, Java, and Obj-C). Existing build and deployment scripts/processes. Etc. Switching languages just isn't in the cards right now. – Steve Sep 05 '11 at 21:22
  • Changed the title to something more descriptive (and easier to google for), if you don't mind. – Alexandre C. Sep 05 '11 at 21:44
  • Thanks Alexandre! That *is* much better. – Steve Sep 05 '11 at 21:50

3 Answers3

41

Yes, the C standard specifically guarantees that this will work.

(C1x §6.7.2.1.13: "A pointer to a structure object, suitably converted, points to its initial member ... and vice versa. There may be unnamed padding within as structure object, but not at its beginning.")

phoxis
  • 60,131
  • 14
  • 81
  • 117
hmakholm left over Monica
  • 23,074
  • 3
  • 51
  • 73
2

The whole gtk+ is implemented like that. I cannot think of a better example. Take a look at http://git.gnome.org/browse/gtk+/tree/gtk/

debleek63
  • 1,181
  • 8
  • 17
1

I'm not disagreeing with any of the answers saying that what you suggested will work, but in the interests of a more complete discussion (without suggesting you use C++!), why not do something like

typedef struct Base ...  /* The types defined exactly as before */
typedef struct Foo ...
typedef struct Bar ...

/* The function takes a Base* since that is what it actually works with*/
void MethodOperatesOnBase(Base* pbase)
{
    /* Do something... */
}

/* Now call it like this: */
Foo foo;
Bar bar;

MethodOperatesOnBase(&foo.base);
MethodOperatesOnBase(&bar.base);

Is there some reason that won't work and you need to use void *? I don't see that this is much more work and it does have the advantage of type-safety.

AAT
  • 3,286
  • 1
  • 22
  • 26
  • Well, In my particular case, the reason is because `Foo` and `Bar` are being exposed as opaque pointers. – Steve Sep 05 '11 at 21:39
  • 1
    @ Steve: OK, so the caller isn't able to make the reference to the `base` member and you have to do it by reinterpreting the struct pointer. That makes sense, thanks for clarifying. – AAT Sep 05 '11 at 21:48