Упаковка структуры: как добавить элементы структуры в начале?

Я реализую двоичное дерево в C89 и пытаюсь использовать общие атрибуты для всех структур узлов посредством композиции. Таким образом, у меня есть следующий код:

enum foo_type
{
    FOO_TYPE_A,
    FOO_TYPE_B
};

struct foo {
    enum foo_type type;
};

struct foo_type_a {
    struct foo base;
    struct foo * ptr;
};

struct foo_type_b {
    struct foo base;
    char * text;
};

Я включаю член типа struct foo во все определения структур в качестве их начального члена, чтобы обеспечить доступ к значению, хранящемуся в enum foo_type, независимо от типа структуры. Для этого я ожидаю, что указатель на объект структуры указывает на его начальный элемент, но я не уверен, что это предположение выполняется в данном случае. Для C99 в стандарте указано следующее (см. ISO/IEC 9899:1999 6.7.2.1 §13).

A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

Хотя все структуры имеют общий объект struct foo в качестве начального члена, в игру вступает заполнение. В то время как struct foo имеет только один член, который имеет размер int, оба struct foo_type_a и struct foo_type_b включают в себя члены-указатели, которые в некоторых случаях увеличивают выравнивание и, таким образом, добавляют отступы.

Итак, учитывая этот сценарий, гарантирует ли язык программирования C (C89 или любая последующая версия) безопасный доступ к значению struct foo::type через указатель на объект, независимо от того, имеет ли этот объект тип struct foo или включает объект типа struct foo как его первый член, например struct foo_type_a или struct foo_type_b?

struct foo является всегда одним и тем же, независимо от того, находится ли он в другой структуре или сам по себе. Другие члены структуры не могут сделать так, чтобы два экземпляра struct foo имели разное расположение в памяти.
UnholySheep 07.04.2019 17:09

@n.m.: Больше кода не нужно. Понятно, что они хотят знать, гарантирует ли C 1989, что преобразование указателя на структуру в указатель на тип ее первого члена дает указатель на ее первый член.

Eric Postpischil 07.04.2019 17:19

Я должен спросить, почему вы разрабатываете новый код в 1989 C?

Eric Postpischil 07.04.2019 17:20

Да, это так. Приведенная гарантия, наряду с гарантией того, что все типы pointer-to-struct должны иметь одинаковое представление, является основой для псевдонима типа соответствующий. См. Что такое строгое правило псевдонимов?

Mark Benningfield 07.04.2019 17:35

@UnholySheep меня беспокоит то, что в стандарте C также содержится предостережение о том, что объекты должны быть «подходящим образом преобразованы», и я не уверен, представляют ли простые приведения типов указателей подходящее преобразование. Например, если по какой-то причине выравнивание изменено с 4 на 8, а магия порядка байтов перемещает значащие биты, то наивные приведения могут вызвать проблемы.

RAM 07.04.2019 17:45

@EricPostpischil Я нацелен на C89, потому что в последний раз, когда я проверял (по общему признанию, это было некоторое время назад), полное соответствие C99 и более поздним версиям, к сожалению, не широко распространено среди установленных компиляторов C.

RAM 07.04.2019 18:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как вы сами цитируете из стандарта C, то, что вы описываете, поддерживается C99 и более поздними версиями.

Похоже, что он также поддерживался C89, поскольку цитируемый вами язык уже присутствовал в документе ANSI-C от 1988 года:

3.5.2.1 Structure and union specifiers

...

Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may therefore be unnamed holes within a structure object, but not at its beginning, as necessary to achieve the appropriate alignment.

Но разве это условие не зависит от того, "подходящим образом литой" указатель? Если бы все, что нужно, это простое приведение между указателями на структуры, то я полагаю, что не было бы необходимости правильно указывать важность приведения объектов.

RAM 07.04.2019 17:56

@RAM: «Подходящее приведение» просто означает приведение к правильному типу. Если бы к требованиям конверсии было что-то более обременительное, это бы о чем-то сказало.

Eric Postpischil 07.04.2019 18:23

@RAM: на языке стандарта был написан для описания, учитывая union STOOGE { MOE moe; LARRY larry; CURLY curly;} *p;, (MOE*)p будет эквивалентно p->moe; оба дадут указатель, который может проверять любые члены MOE, которые являются частью общей начальной последовательности, совместно используемой с любым объектом, находящимся в *p. В интерпретации стандарта, используемого gcc и clang, (MOE*)p и p->moe по-прежнему эквивалентны, но они обе дают указатели, которые должны быть либо преобразованы в указатели на символы перед использованием, либо использованы с такими функциями, как memcpy , которые будут обрабатывать их как указатели на символы.

supercat 11.04.2019 21:33

Другие вопросы по теме