Я реализую двоичное дерево в 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
?
@n.m.: Больше кода не нужно. Понятно, что они хотят знать, гарантирует ли C 1989, что преобразование указателя на структуру в указатель на тип ее первого члена дает указатель на ее первый член.
Я должен спросить, почему вы разрабатываете новый код в 1989 C?
Да, это так. Приведенная гарантия, наряду с гарантией того, что все типы pointer-to-struct
должны иметь одинаковое представление, является основой для псевдонима типа соответствующий. См. Что такое строгое правило псевдонимов?
@UnholySheep меня беспокоит то, что в стандарте C также содержится предостережение о том, что объекты должны быть «подходящим образом преобразованы», и я не уверен, представляют ли простые приведения типов указателей подходящее преобразование. Например, если по какой-то причине выравнивание изменено с 4 на 8, а магия порядка байтов перемещает значащие биты, то наивные приведения могут вызвать проблемы.
@EricPostpischil Я нацелен на C89, потому что в последний раз, когда я проверял (по общему признанию, это было некоторое время назад), полное соответствие C99 и более поздним версиям, к сожалению, не широко распространено среди установленных компиляторов C.
Как вы сами цитируете из стандарта 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: «Подходящее приведение» просто означает приведение к правильному типу. Если бы к требованиям конверсии было что-то более обременительное, это бы о чем-то сказало.
@RAM: на языке стандарта был написан для описания, учитывая union STOOGE { MOE moe; LARRY larry; CURLY curly;} *p;
, (MOE*)p
будет эквивалентно p->moe
; оба дадут указатель, который может проверять любые члены MOE
, которые являются частью общей начальной последовательности, совместно используемой с любым объектом, находящимся в *p
. В интерпретации стандарта, используемого gcc и clang, (MOE*)p
и p->moe
по-прежнему эквивалентны, но они обе дают указатели, которые должны быть либо преобразованы в указатели на символы перед использованием, либо использованы с такими функциями, как memcpy
, которые будут обрабатывать их как указатели на символы.
struct foo
является всегда одним и тем же, независимо от того, находится ли он в другой структуре или сам по себе. Другие члены структуры не могут сделать так, чтобы два экземпляраstruct foo
имели разное расположение в памяти.