В C, указатель на структуру всегда указывает на его первый член?
Предположим, что у меня есть несколько C-структур, для которых я хотел бы использовать определенный набор функций.
Мне интересно, является ли законным подход:
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++;
}
В этом примере вы заметите, что обе структуры Foo
и Bar
начинаются с члена Base
.
И, что в MethodOperatesOnBase
, я применил параметр void *
к Base *
.
Я хотел бы передать указатели на Bar
и указатели на Foo
на этот метод и полагаться на первый член структуры как структуру Base
.
Является ли это приемлемым или есть некоторые (возможно, специфичные для компилятора) проблемы, о которых мне нужно знать? (Например, какая-то схема упаковки/заполнения, которая изменит местоположение первого члена структуры?)
Ответы
Ответ 1
Да, стандарт C гарантирует, что это будет работать.
(C1x §6.7.2.1.13: "Указатель на структурный объект, соответствующим образом преобразованный, указывает на его начальный член... и наоборот. В качестве структурного объекта может быть неназванное дополнение, но не в его начале." )
Ответ 2
Я не согласен ни с одним из ответов, говоря, что то, что вы предложили, будет работать, но в интересах более полного обсуждения (не предлагая использовать С++!), почему бы не сделать что-то вроде
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);
Есть ли какая-то причина, которая не будет работать, и вам нужно использовать void *
? Я не вижу, что это гораздо больше работы, и у нее есть преимущество безопасности типов.
Ответ 3
Весь gtk + реализуется так. Я не могу придумать лучшего примера. Взгляните на http://git.gnome.org/browse/gtk+/tree/gtk/