Reputation: 871
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
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