2012-04-29 5 views
0

У меня есть вопрос о C и попытка издеваться над частичным типом «наследования», только при доступе к членам структур. Посмотрите на следующий пример:Отказывающееся «наследование» при доступе к элементам структур в C

#pragma pack(push,1) 
typedef struct foo 
{ 
    int value; 
    int value2; 
}foo; 
typedef struct foo_extended 
{ 
    // "inherits" foo 
    int value; 
    int value2; 
    // "inherits" foo stops 
    //we also have some additional data 
    float additional; 
}foo_extended; 
#pragma pack(pop) 


//! This function works for both foo types 
void workboth(void* objP) 
{ 
    foo* obj = (foo*)objP; 

    obj->value = 5; 
    obj->value2 = 15; 
} 

//! This works only for the extended 
void workextended(foo_extended* obj) 
{ 
    obj->value = 25; 
    obj->value2 = 35; 
    obj->additional = 3.14; 
} 

int main() 
{ 
    foo a; 
    foo_extended b; 

    workboth(&a); 
    workboth(&b); 
    workextended(&b); 

    return 0; 
} 

Это работает в моей системе, но мой вопрос, может ли это быть портативными, пока есть правильная упаковка вовлеченных структур (в зависимости от компилятора). Полагаю, для этого нужно было бы #ifndefs корректно использовать плотную упаковку в других компиляторах.

Конечно, очевидная проблема - полное отсутствие проверки типа и возложение всей ответственности за правильное использование программиста, но мне интересно, переносимо это или нет. Благодаря!

P.S .: Забыл упомянуть, что стандарт я стараюсь придерживаться является C99

ответ

0

При чтении 6.7.2.1/12 и/13 в C99 draft, я думаю, можно предположить, что две разные структуры с одинаковыми начальными элементами совместимы с первым различным членом.

6.7.2.1/12

Каждый член небитового-поле структуры или объединения объекта выравнивается в определенном реализацией способом, соответствующим его типу.

6.7.2.1/13

В пределах объекта структуры, члены небитового поля и единицы, в которых бит-поля находятся имеют адреса, которые увеличивают в том порядке, в котором они были объявлены , Указатель на объект структуры, соответствующим образом преобразованный, указывает на его первоначальный член (или если этот элемент является битовым полем, а затем блоку, в котором он находится) и наоборот. В структурном объекте может быть неназванное дополнение, но не в начале.

+0

Спасибо. Это отвечает на мой вопрос, так как я хочу быть совместимым с C99. Забыл упомянуть об этом в моем вопросе – Lefteris

+0

Хм, я не вижу, как вы делаете свое заключение из текста, который вы цитируете. Это только гарантирует, что их порядок будет одинаковым и что выравнивание подходит для типа. Он ничего не говорит о том, что внутреннее заполнение 'foo' может не измениться, если разные поля отличаются. –

+0

Да, конечно @JensGustedt. Но реализация должна быть активно программиста недружелюбной, чтобы иметь разные детали реализации для структур, скажем, с помощью 2 или 3 menbers ... – pmg

1

У меня есть немного другой метод. Вместо того, чтобы использовать одни и те же значения, я создаю-структуру в структуры, и таким образом упаковка ненужно:

typedef struct foo 
{ 
    int value; 
    int value2; 
}foo; 

typedef struct foo_extended 
{ 
    foo father; 
    float additional; 
}foo_extended; 

теперь остальное довольно много, как вы показали, с небольшой разницей:

void workextended(foo_extended* obj) 
{ 
    obj->father.value = 25; 
    obj->father.value2 = 35; 
    obj->additional = 3.14; 
} 

, но я бы добавил id как поле первого объекта в иерархии, чтобы убедиться, что кастинг сделан для правильного объекта.

Этот метод гарантированно работает по стандарту C.

+0

Спасибо за ответ. Я знаю об этом методе, но проблема с этим методом заключается в том, что вам нужно сделать obj-> father.value, как вы показали. – Lefteris

1

По С11, а также при поддержке некоторых существующих компиляторов как расширения старых стандартов, вы должны использовать анонимный struct для этого

struct foo_extended { 
    struct { 
     int value; 
     int value2; 
    }; 
    //we also have some additional data 
    float additional; 
}; 

этих вашей субструктуры имеет точно такое же расположение, как foo в в частности, что касается выравнивания его частей: быть совместимым между различными единицами компиляции struct, которые имеют одинаковые поля в том же порядке, должны быть одинаковыми.

(Воздействие вашей упакованной прагмы не очень понятно мне)

С вашей foo структуры является первым в foo_extended она всегда должна быть со смещением 0 в том.

+0

Это был бы лучший выбор, я полагаю. Проблема в том, что это C11. Поэтому, только по соображениям совместимости, я думаю, что я должен придерживаться своего метода. Поправьте меня, если мое мышление здесь неверно. Упакованная прагма существует там, где есть несколько раз, если я не ставил ее там, компилятор упаковывает эти структуры по-разному, и я получаю неправильные данные при разыменовании указателя. – Lefteris

+0

Я не думаю, что C11 слишком ограничивает здесь. gcc (+ родственники) и компиляторы Microsoft поддерживают это. Таким образом, вы были бы в значительной степени охвачены :) –

+0

Хорошо, что вы упомянули об этом в ответ, потому что мне было интересно об этом. О, хорошо, это небольшое изменение только в определении каждой структуры, которое нужно было бы сделать, чтобы это было легко выполнимо и в будущем. Нужно исследовать его немного больше. Помогло бы избавиться от досадных упаковочных прагм – Lefteris

Смежные вопросы