3

Hm... why is it that, when I print sizeof(struct MyStruct), it outputs 3 (instead of 2) for this code?

#pragma pack(push, 1)
    struct MyStruct
    {
        unsigned char a : 6;
        union
        {
            struct
            {
                unsigned int b : 9;
            };
        };
    };
#pragma pack(pop)

In case it matters, I'm running MinGW GCC 4.5.0 on Windows 7 x64, but honestly, the result is weird enough for me that I don't think the compiler and the OS matter too much here. :\

timrau
  • 22,578
  • 4
  • 51
  • 64
user541686
  • 205,094
  • 128
  • 528
  • 886

2 Answers2

13

You can't have the field starting at an address that is not byte aligned. You're expecting:

6 bits + 9 bits -> 15 bits -> 2 bytes

but what you're getting is:

6 bits -> 1 byte
9 bits -> 2 bytes
total ->  3 bytes

The data is being stored as:

| 1 byte | 2 byte |3 byte | 
 aaaaaaXX bbbbbbbb bXXXXX  

when you were expecting:

| 1 byte | 2 byte |
 aaaaaabb bbbbbbbX  

edit: To clarify based on the comments below:

The union (and the containing struct) must be byte aligned. It doesn't matter that the contents are only 9 bits, the union/struct itself is a full 16 bits. Notice that you cannot do the following:

struct MyStruct
{
    unsigned char a : 6;
    union
    {
        struct
        {
            unsigned int b : 9;
        } c:9;
    } d:9;
};

As C won't let you specify the entire struct's bit-size.

nss
  • 541
  • 3
  • 9
  • Yeah, but why? Isn't the whole point of bit fields that they're not byte-aligned? (It's not like two 1-bit fields placed next to each other are both byte-aligned, so why is this any different?) – user541686 Feb 20 '11 at 04:04
  • Haha, that's a fair point. It's really implementation dependent. What's your compiler doing with: struct MyStruct { unsigned char a : 6; unsigned int b : 9; }; ? – nss Feb 20 '11 at 04:09
  • It's giving a 2... (wouldn't bit fields be kind of pointless otherwise?) – user541686 Feb 20 '11 at 04:14
  • yes. :) It's because of the struct and/or the union. I believe your compiler is requiring those to begin at byte aligned addresses. I just tried this out: http://codepad.org/xWbrGP1g ... note that whatever compiler codepad.org uses behaves similarly (though seems to require things to be word-aligned). Again, it's implementation dependent. To clarify, the struct itself must take up the full int size. – nss Feb 20 '11 at 04:20
  • Well, I mean, I see *what* is happening with the alignment, but the question of "Why?" is still lingering in my head. Like, *why* should the structure be word-aligned, if it has bit fields? Does that have any benefits in any situations, or is that just an arbitrary requirement for no good reason? – user541686 Feb 22 '11 at 03:54
  • So I don't know for certain without actually digging into GCC's source and seeing exactly how they're implemented, but normally for bitfields, the compiler is doing all the bitwise masking and shifting for you. What you're wanting it to do is take an arbitrary number of bytes (the size of the struct) and shift them all to the correct (readable) alignment and interpret that as a struct. This doesn't sound terribly performant and so it may not have been regarded as useful to the compiler authors. Normally when reading a bitfield, only two words at most need to be read, shifted, and masked. – nss Feb 22 '11 at 22:02
  • I did some digging and found the specification at http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf . Section 6.7.2.1 says "A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type". The struct is not a bit field and is thus not required to be tightly packed off-alignment. I figure the implementation complexity was not worth it. – nss Feb 23 '11 at 00:57
0

Adding to the answer given by @nss -- my apologies, this would've been a comment if comments weren't so limited on formatting:

#include <stdlib.h>

struct Test {
  unsigned short x : 6;
  unsigned short y : 1;
  unsigned short z;
};

int main( int argc, char *argv[] ) {
  printf( "sizeof( Test ) = %d\n", sizeof( struct Test ) );

  return 0;
}

It prints '4' for the size. I tested with gcc, g++, and Sun Studio's CC and cc.

Not that I recommend doing what you're attempting to do, but you could probably do what you're attempting to do with a union. I've seen (but not written myself) code that looked like this:

struct Test {
  unsigned short x1 : 6;
  unsigned short x2 : 3;
                    : 1; // unused
  unsigned short x3 : 4;
  // ...
};

I might have the syntax slightly wrong there ... but I don't think so.

Point being: create two separate structs (or a struct and a union) with the layout you were going for, then insert some dummy members where they should overlap, and union those together.

Brian Vandenberg
  • 4,011
  • 2
  • 37
  • 53