Pergunta

I'm a bit puzzled by how the following code gets layed out in memory:

struct Thing
{
    union
    {
        unsigned value:24;
        uint8_t bytes[3];
    };
    Thing(int v)
            :value(v)
    {}

    void foo()
    {
        printf("Thing %p value=%d !\n", this, value);
    }

} __attribute__((__packed__));

On gcc 3.3, 4.3 or 4.6 on Linux (without any special options I can think of - only "-Wall -g" on 4.6), the size of the structure is always 4:

$ pahole ./union
struct Thing {
        union {
                unsigned int               value;                /*           4 */
                unsigned char              bytes[3];             /*           3 */
        };
[...]

We had some similar code here where we had the unsigned value:24 in a structure, and somebody added the union and inadvertently increased the size of the struct from 3 to 4 bytes. The same thing happens if I try to define the union as "packed" - size is still 4. Is this behavior as per the C++ spec? What would be the explanation?

later edit: replaced "C spec" with "C++ spec".

Foi útil?

Solução

You missed the packed attributes to your anonymous union. Consider this example:

#define PACKED __attribute__((__packed__))
struct S1 { unsigned value:24; } PACKED ;
struct S2 { union { char a[3]; unsigned value:24; };  } PACKED ;
struct S3 { union { char a[3]; unsigned value:24; } PACKED ;  };


int main() {
   std::cout << sizeof(S1) << std::endl;
   std::cout << sizeof(S2) << std::endl;
   std::cout << sizeof(S3) << std::endl;
}

Output:

3
4
3

The packed attribute is a little weird, I always try and test every possible combination to get proper result.

Outras dicas

Well, firstly, __attribute__((__packed__)) is a gcc extension so the standard doesn't have anything to say about what might or might not happen with that.

In general however, the compiler is allowed to insert any padding it feels appropriate between bit fields. In particular, I've seen compilers that given this:

 struct
 {
     short a : 8;
     int b : 8;
 }

will align b on a 32 bit boundary.

Basically, if you use bitfields you're on your own. There's no guarantee of order of the fields or of padding. The only guarantee you have is the size of the bitfield.

The question is tagged as C++, but you mention C spec. AFAIK, there is difference between C and C++ in that regard. In C, bitfields can be only of integer type, while C++ allows any integral type.

As bitfields are mapped to integral types, I would expect that the size of a bitfield is always 1, 2 or 4.

Therefore, this would give you sizeof 3:

struct Thing
{
    union
    {
        // without 'short' int would be assumed -> 4 bytes
        unsigned short value:15; // -> maps to short -> 2 bytes
        uint8_t bytes[3];        // -> 3 bytes, so the whole union is 3 bytes long
    };
}

With value:24, it will always be mapped to nearest integral type, i.e. integer.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top