Почему адрес члена структуры в объединении лежит так? - PullRequest
0 голосов
/ 03 марта 2020

У меня есть базовое представление о том, каким будет адрес члена в объединении. Недавно я обнаружил структуру данных, такую ​​как:

typedef union {
    char data[16];

    struct {
        uint8_t filler[15],
            /* how many free bytes in this stack allocated string
            * same idea as fbstring
            */
            space_left : 4,
            /* if it is on heap, set to 1 */
            is_ptr : 1, flag1 : 1, flag2 : 1, flag3 : 1;
    };

    /* heap allocated */
    struct {
        char *ptr;
        /* supports strings up to 2^54 - 1 bytes */
        size_t size : 54,
            /* capacity is always a power of 2 (unsigned)-1 */
            capacity : 6;
        /* the last 4 bits are important flags */
    };
} xs;

Затем я печатаю адрес большинства членов, например:

(gdb) p &string
$1 = (xs *) 0x7fffffffdc20
(gdb) p &string->data
$2 = (char (*)[16]) 0x7fffffffdc20
(gdb) p &string->filler
$3 = (uint8_t (*)[15]) 0x7fffffffdc20
(gdb) p &string->space_left
$4 = (uint8_t *) 0x7fffffffdc2f ""
(gdb) p &string->is_ptr
$5 = (uint8_t *) 0x7fffffffdc2f ""
(gdb) p &string->flag1
$6 = (uint8_t *) 0x7fffffffdc2f ""
(gdb) p &string->ptr
$7 = (char **) 0x7fffffffdc20
(gdb) p &string->size
$8 = (size_t *) 0x7fffffffdc28
(gdb) p &string->capacity
$9 = (size_t *) 0x7fffffffdc28
(gdb) p &string->flag2
$10 = (uint8_t *) 0x7fffffffdc2f ""
(gdb) 

Я могу понять, почему массив data, filler и ptr начать с того же адреса.

Но почему space_left, is_ptr ... начать с того же адреса? Кроме того, почему смещение между space_left и filler составляет 16 вместо 8 * 15?

1 Ответ

1 голос
/ 03 марта 2020

По умышленному или счастливому стечению обстоятельств вы выбрали пример 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.

Надеюсь, это захватило обсуждение из ранее. Дайте мне знать, если у вас есть дополнительные вопросы.

...