Steven
Steven

Reputation: 871

Why address of struct's member in union lay like this?

I have basic idea about how address of member in union would be. Recently I found a data structure like following:

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;

Then I print most member's address like:

(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) 

I can understand why array data, filler and ptr start the same address.

But why space_left,is_ptr ... start the same address? Also, why the offset between space_left and filler is 16 instead of 8*15?

Upvotes: 0

Views: 117

Answers (1)

David C. Rankin
David C. Rankin

Reputation: 84599

Whether by intent or happy coincidence, you have chosen an example of union and stuct to learn from that covers most of the features of each -- even if that does make it a bit more complicated to digest all at one time.

union

The primary difference between a union and struct is that a union only provides a single block of storage capable of storing the largest of all of its members, and the value of the union is that of the last value stored. A union can only store the value of one of its members at any given point in time, but the bytes that make up the last value stored may be viewed through any of its members. All members of a union share the same starting address. See: C11 Standard - 6.5.8 Relational operators(p5) "...All pointers to members of the same union object compare equal. ..."

struct

By contrast, a stuct provides storage for each of its members and can store each member independent of any other. The size of a struct is the sum of the size of each of the members plus any padding bytes necessary to maintain proper alignment of members. (padding bytes may appear between or after any member but not before the first -- the address of a struct is the address of its first member) Where padding occurs is not specified by the standard so compilers are free to place padding byte however they choose to implement the struct.

Additionally, both a union and struct can contain bit-fields specified with the ':' followed by the number of bits to associate with a given field. The bit fields are created from a single member. In your case above the size_t size : 54, capacity : 6; creates a bit-field from a single size_t member allowing access to the first 54 bits through size and the following 6 bits through capacity. Both size and capacity are fields within the same size_t member.

Why Some Members Have the Same Address and Some Don't?

The easiest way to understand this is to take what you have as an example. You declare 1-union. It will have storage for the largest of char data[16]; or struct {uint8_t filler[15], space_left : 4, is_ptr : 1, flag1 : 1, flag2 : 1, flag3 : 1; } (which is a 15-byte array + 1-byte split into a bit-field) or struct { char *ptr, size_t size :54, capacity : 6; } All can fit into 16-bytes -- so that should be the total size of the union. data and each struct will share the same starting address -- that being the address of the union. See, e.g. C11 Standard - 6.5.2.3 Structure and union members

Within each struct you will have a separate address for each separate member (other than the first) -- subject to the bit-field discussion above. Your first struct has a uint8_t filler[15] array and then a one-byte bit-field space_left. The address of filler will be the address of the union. The address of uint8_t space_left will have an address of its own. For your second struct, you have a pointer ptr and then a size_t size also made into a bit-field. ptr will have the address of the union and size will have an addresss of its own.

(as noted by JohnathanLeffler, you should not attempt to take the address of the individual bit-fields within any member)

So why do the structs and each of their first members have same address as the union? Recall, a union is a single block of storage for the largest member. All union members will share the same starting address. Also recall that the address of a struct is the address of the first member, so each of struct and the first members of each struct within the union will also share the union address as their address. That leaves only additional members of each struct having their own address, but as noted in the comment you should not attempt to take the address of the individual bit-fields as they are created from a single struct-member.

Hopefully this has captured the discussion from earlier. Let me know if you have further questions.

Upvotes: 1

Related Questions