Size of packed struct with union of bit fields less than 8 bits in C
Asked Answered
S

1

5

Is it possible in C to get the size of the following structure to be 2?

#include <stdio.h>

struct union_struct {
    char foo;
    char bar : 2;
    union {
        char foobar1 : 6;
        char foobar2 : 6;
    };
};

int main(void)
{
    printf("size of union struct: %d\n", sizeof(struct union_struct));
    return 0;
}

output, compiled with gcc:

size of union struct: 3

Sir answered 5/9, 2013 at 23:12 Comment(0)
F
6

If you are relying on implementation defined behavior, then yes, but you have to organize it a bit differently:

#ifdef UNNAMED_BITFIELDS_ARE_WELL_DEFINED
#define ANON
#else
#define ANON3(X) anonymous__## X ##__
#define ANON2(X) ANON3(X)
#define ANON ANON2(__LINE__)
#endif

struct union_struct {
    char foo;
    union {
        struct {
            char bar     : 2;
            char ANON    : 6;
        };
        struct {
            char ANON    : 2;
            char foobar1 : 6;
        };
        struct {
            char ANON    : 2;
            char foobar2 : 6;
        };
    };
};

The first byte is foo, the second byte is the anonymous union. Then anonymous union has 3 single byte anonymous structs. Each struct (optionally) uses unnamed bit-fields to allow foobar1 and foobar2 to represent the same 6 bits that follow bar.

From my understanding of the C.11 standard the above code is correct when UNNAMED_BITFIELDS_ARE_WELL_DEFINED is defined. However, there seems to be debate on whether or not unnamed bit-fields have well-defined semantics (see comments below). If unnamed bit-fields do not have well-defined semantics, then the code above can expand each ANON macro into a name for the bit-field.

However, the C.11 standard only defines bit-fields on _Bool, int, and unsigned, while the use of any other type for a bit-field is implementation defined (C.11 §6.7.2.1 ¶5).

Formerly answered 5/9, 2013 at 23:23 Comment(9)
Aren't both uses of packed no-ops?Belgae
@R..: Hmm... On my system, I can remove both, and it still has size 2. Thanks, answer updated.Formerly
the packed attributes were just part of my attempt to get it down to 2 bytesSir
@croyd: I have removed their use in the answer.Formerly
Is this well-defined by the C standard to have the same semantics as the original definition? In the original, if a value is assigned to bar and then a value is assigned to foobar1, bar is still valid. In the new definition, when a value is assigned to foobar1, it is modifying a different union member than bar, and I do not see any obligation for the C implementation to preserve the value of bar during this assignment. E.g., an implementation may implement an assignment to foobar1 by writing to all eight bits of the union, disregarding bar.Haas
@jxh: When the first two bits are an unnamed bit-field, there is no guarantee they will not be disturbed when the other six bits are assigned. In this case, type-punning is not relevant; the question is not whether the byte will be reinterpreted as another type, but whether assigning to a union member changes the unnamed bit-field. 6.5.2.3 6 says that common initial sequence may be inspected. There is no guarantee that modifying a named bit-field will not alter an unnamed bit-field in the same structure.Haas
@jxh: Yes, anything can happen to an unnamed bit-field. This is due to the “as if” rule. Consider just the innermost struct for the moment; ignore the enclosing union. In this struct, the unnamed bit field cannot be inspected by the program, since it has no name. The implementation may do anything it wants with those bits. (Just as if you had an int x and never initialized, assigned, or inspect it; the implementation could put anything it wanted in x.) Now look at the union. When you change one of members in the union, one of the structs, that becomes the only member in the union.…Haas
When you modify the struct, none of other union members exist any longer. Yes, the struct may have had some value in the unnamed bit field, so inspecting the struct via memcpy might have shown some particular value due to reinterpreting the result of assigning to another struct. However, once an assignment is made to a new struct, that assignment is permitted to alter the unnamed bit field, because the unnamed bit field has no defined value in C semantics. So this code is just wrong; it does not provide the same semantics as the original code.Haas
6.5.2.3 5 is irrelevant; it deals with accessing a member. I am not concerned with accessing or inspecting a member. I claim that even if writing to x.bar causes the unnamed bit-field in the other union members to have a value, modifying x.foobar1 is permitted to alter the unnamed bit-field in any way. Once you modify x.foobar1, the contents of the union are the struct object that contains foobar1. The struct object that contains bar no longer exists, and there is no defined behavior for the value of the unnamed bit-field that exist alongside foobar1.Haas

© 2022 - 2024 — McMap. All rights reserved.