Порядок полей в структурах C / C ++ - PullRequest
1 голос
/ 02 февраля 2012

У меня есть ситуация, похожая на эту

struct Child
{
  u16 x, y;
  // other fields
};

struct Father
{
  struct Child child1;
  struct Child child2;
  // other fields
};

Father tilemap[WIDTH][HEIGHT];

Теперь я только что понял, что хотел бы сохранить четыре байта для x, y, которые всегда имеют одинаковые значения для обоих потомков одного и того же отца.

По всему моему коду я передаю множество Father* и множество Child*, восстанавливая координаты с father->child1->x или child1->x соответственно. Я хотел бы безопасно переместить координаты на уровне Father, но я не уверен в некоторых фактах.

Будет ли соблюден порядок объявленных полей по сравнению с какой-либо оптимизацией или возможной реализацией gcc / g ++? Могу ли я быть уверен, что &father == &father.child1?

Настоящая проблема в том, что я передаю Child*, не зная, является ли это полем child1 или child2, поэтому я не могу напрямую узнать смещение для восстановления адреса отца (и, следовательно, координат) .. Child уровень, чтобы различать их, но смогу ли я тогда легко восстановить адрес отца?

Любое предложение будет оценено, спасибо

РЕДАКТИРОВАТЬ: просто как дополнительная информация, я использую C ++ в качестве основного языка, но эти структуры не содержат каких-либо странных методов, только поля и пустой конструктор.

Ответы [ 6 ]

4 голосов
/ 02 февраля 2012

Общие правила размещения полей в C:

  1. Адрес первого члена совпадает с адресом самой структуры. То есть offsetof поля члена равно 0.
  2. Адреса членов всегда увеличиваются в порядке декларирования. То есть offsetof n-го поля ниже, чем у (n + 1) -го члена.

Конечно, в C ++ это верно только в том случае, если это тип стандартная компоновка , то есть класс или структура без открытых / закрытых / защищенных смешанных членов, без виртуальных функций и члены, унаследованные от других классов.

2 голосов
/ 02 февраля 2012

Отказ от ответственности: Частичный ответ. Только C ++

Будет ли порядок объявленных полей соблюдаться по сравнению с любой оптимизацией или возможная реализация gcc / g ++?

Порядок членов в макете памяти не будет изменен компилятором. Это тот же порядок, в котором вы объявили членов.

Могу ли я быть уверен, что & папа == & папа.чайлд1?

В данном конкретном случае да. Но это не следует из самого факта, что child1 - первый член отца, который &father == &father.child1?. Это верно только в том случае, если отцом является POD , что в данном случае является

.
1 голос
/ 02 февраля 2012

В соответствующем разделе стандарта C говорится следующее (выделено мной):

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

Стандарт C ++ дает то же обещание:

Указатель на структуру стандартного макетаОбъект, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на свой начальный элемент (или, если этот элемент является битовым полем, то на модуль, в котором он находится), и наоборот.[Примечание: Следовательно, в объекте структуры стандартной компоновки может быть безымянный отступ, но не в его начале, что необходимо для достижения соответствующего выравнивания.—Конечная заметка]


Поэтому, когда вы спросите:

Могу ли я быть уверен, что &father == &father.child1?

Ответэто да.

0 голосов
/ 19 мая 2015

Если вы решили полагаться на какое-либо специфическое поведение компилятора, лучшее, что вы можете сделать, это добавить статические утверждения для любых допущений, на которые вы полагаетесь. Таким образом, если что-то изменится в макете из-за обновления компилятора, параметров компиляции или прагм в других частях кода, вам сразу сообщат во время компиляции, и в чем именно заключается проблема. Тогда это послужит основой для переноса на другие платформы, если это будет требованием.

Примеры в этом вопросе: Что делает static_assert и для чего вы его используете?

0 голосов
/ 02 февраля 2012

Вместо того, чтобы полагаться на макет памяти, вы рассматривали возможность использования чего-либо в соответствии с шаблоном Flyweight ?

Вместо того, чтобы хранить два Childs в Father, вы можете хранить два урезанныхBasicChilds (которые не имеют данных x, y) и генерируют полноценные Childs на лету по мере необходимости:

struct BasicChild
{
    float foo;
    float bar;
    void printPosition(int x, int y) {std::cout << x << "," << y << "\n";}
};

struct Child
{
    Child(BasicChild basicChild, int x, int y)
        : basicChild_(basicChild), x_(x), y_(y){}
    float foo() {return basicChild_.foo;}
    float bar() {return basicChild_.bar;}
    void printPosition() {basicChild_.printPosition(x_, y_);}

private:
    int x_, y_;
    BasicChild basicChild_;
};

struct Father
{
    Child child1() {return Child(child1_, x_, y_);}
    Child child2() {return Child(child2_, x_, y_);}

private:
    int x_, y_;
    BasicChild child1_;
    BasicChild child2_;
};
0 голосов
/ 02 февраля 2012

Попробуйте следующее

struct Child
{
  int isChild1;
  u16 x, y;
  // other fields
};
...
Father *father_p;
if (*child_p).isChild1
   father_p = child_p;
else
   father_p = child_p - sizeof(struct Child);
(*father_p).x = ... // whatever you want to do with coordinates

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...