По умышленному или счастливому стечению обстоятельств вы выбрали пример union
и stuct
, чтобы извлечь уроки, охватывающие большинство функций каждой из них, даже если это немного усложняет процесс один раз.
union
Основное различие между union
и struct
состоит в том, что union
предоставляет только один блок хранения, способный хранить самый большой из всех его членов, и значение union
равно значению последнего сохраненного значения. union
может хранить значение только одного из его элементов в любой данный момент времени, но байты, составляющие последнее сохраненное значение, могут просматриваться любым из его элементов. Все члены union
имеют один и тот же начальный адрес. См .: C11 Standard - 6.5.8 Реляционные операторы (p5) "...All pointers to members of the same union object compare equal. ..."
struct
В отличие от stuct
обеспечивает хранение для каждого из его членов и может хранить каждого члена независимо от любого другого. Размер struct
является суммой размера каждого из элементов плюс любые байты заполнения, необходимые для поддержания правильного выравнивания элементов. (байты заполнения могут появляться между любым элементом или после него, но не перед первым - адрес struct
является адресом его первого члена). Где происходит заполнение, стандарт не определяется, поэтому компиляторы могут свободно размещать байт заполнения они решают реализовать структуру.
Кроме того, и union
, и struct
могут содержать битовые поля , указанные в ':'
, за которыми следует число битов, с которыми необходимо связать данное поле. Битовые поля создаются из одного члена. В вашем случае выше size_t size : 54, capacity : 6;
создает битовое поле из одного size_t
члена, позволяющего получить доступ к первым 54
битам через size
и следующим 6
битам через capacity
. И size
, и capacity
являются полями одного и того же члена size_t
.
Почему некоторые члены имеют одинаковый адрес, а некоторые нет?
Самый простой способ понять это - взять то, что у вас есть, в качестве примера. Вы объявляете 1- union
. У него будет хранилище для самого большого из char data[16];
или struct {uint8_t filler[15], space_left : 4, is_ptr : 1, flag1 : 1, flag2 : 1, flag3 : 1; }
(который представляет собой 15-байтовый массив + 1 байт, разбитый на битовое поле) или struct { char *ptr, size_t size :54, capacity : 6; }
Все может уместиться в 16-байтовые - так что следует быть общий размер союза. data
и каждый struct
будет иметь один и тот же начальный адрес - это адрес union
. См., Например, C11 Standard - 6.5.2.3 Структура и члены объединения
В каждом struct
у вас будет отдельный адрес для каждого отдельного члена (кроме первого) - при условии обсуждение битового поля выше. Ваш первый struct
имеет массив uint8_t filler[15]
, а затем однобайтовое битовое поле space_left
. Адрес filler
будет адресом union
. Адрес uint8_t space_left
будет иметь собственный адрес. Для вашего второго struct
у вас есть указатель ptr
, а затем size_t size
также превращается в битовое поле. ptr
будет иметь адрес union
, а size
будет иметь собственные адреса.
(как отметил JohnathanLeffler , вы не должны пытаться взять адрес отдельных битовых полей в любом элементе)
Так почему же структуры и каждый из их первые участники имеют тот же адрес, что и union
? Напомним, что union
является единым блоком памяти для самого крупного участника. Все участники union
будут иметь один и тот же начальный адрес. Также напомним, что адрес struct
является адресом первого члена, поэтому каждый из struct
и первые члены каждой структуры в union
также будут использовать адрес union
в качестве своего адреса. Это оставляет только дополнительные члены каждого struct
, имеющие свой собственный адрес, но, как отмечено в комментарии, вы не должны пытаться взять адрес отдельных битовых полей, так как они созданы из одного struct-member.
Надеюсь, это захватило обсуждение из ранее. Дайте мне знать, если у вас есть дополнительные вопросы.