4

I use Visual Studio 2013 for a x64 system. I have the following struct:

#pragma pack(1)
    struct TimeStruct
    {
      int milliseconds  : 10;
      BYTE seconds      : 6;    
      BYTE minutes      : 6;    
      BYTE hour         : 5;    
      BYTE day          : 5;    
    };
#pragma pack()

and an array:

TimeStruct stArray[10];

When I use sizeof(stArray);I get 80 as a resault instead of 40.

I need to know if the problem is that the compiler doesn't pack correctly or if sizeof doesn't consider the bitfield's actual size.

Thanks

Evya
  • 73
  • 1
  • 7
  • Maybe you should use `union` instead of `struct`. – Farhad Reza Jan 22 '15 at 14:38
  • 2
    Start with the punt taken by all vendors regarding bit-fields, C++11 § 9.6 [class.bit] : "Allocation of bit-fields within a class object is implementation-defined." No truer words have ever been spoken, as bit fields are are *notoriously* so (implementation-defined). – WhozCraig Jan 22 '15 at 15:01

2 Answers2

4

See What is VC++ doing when packing bitfields? for more explanation on how MSVC treats bitfields.

It's implementation dependent how bitfields are packed and ordered, so be aware that the layout can be different from what you expect, and will depend on the particular compiler you are using.

sizeof() gives you the size of the struct regardless of what its members are, so it's the bitfield packing that's different from your expectations.

You can possibly rason about the layout from the documentation or use this code to empirically discover the layout:

struct TimeStruct a;
unsigned char *b = (unsigned char*)&a;
a.milliseconds = 1;
a.seconds = 2;
a.minutes = 3;
a.hour = 3;
a.day = 4;
for(size_t i = 0; i < sizeof a; i++)
  printf("%02X ", b[i]);

 -> 64 bit compiler 01 00 00 00 02 03 04 05
 -> 32 bit compiler 01 30 FE 00 02 03 A4 65

It appears the struct is allocated an entire int for the 1. int milliseconds : 10; member, and the remaining 3 bytes are packed individually after that, allocating a BYTE for each member, but not combining bits from the different allocated units.

You should be able to pack it tightly with the MSVC compiler if you use int or unsigned int for all the fields, and this struct will take 4 bytes:

struct TimeStruct
{
  int milliseconds : 10;
  int seconds      : 6;    
  int minutes      : 6;    
  int hour         : 5;    
  int day          : 5;    
};
Community
  • 1
  • 1
nos
  • 223,662
  • 58
  • 417
  • 506
  • I certainly didn't down vote this, and would be curious to know what the OP sees when [running this code](http://coliru.stacked-crooked.com/a/a1c6bf422970ea93) – WhozCraig Jan 22 '15 at 14:50
  • Adjacent bit-fields have same memory location. Compiler may allocate them with any size in any order. – Abyx Jan 22 '15 at 14:58
  • @SGrebenkin It packs this struct in 4 bytes with VS2010 and an x64 configuration – nos Jan 22 '15 at 15:07
  • Of course. But you changed the code BYTE to int. It's sequence of fields of one type. So, according to the msdn.microsoft.com/en-us/library/ewwyfdbe.aspx, you'll get 4 bytes – SGrebenkin Jan 22 '15 at 15:10
  • @SGrebenkin Correct. Which seems to provide the questioner with a solution. – nos Jan 22 '15 at 15:18
  • Thanks [nos] !, apparently packing bitfields of different types will cause unexpected behavior as such. When I changed the types to (int) or (WORD), the struct was packed to my expectations of 4 bytes. – Evya Jan 25 '15 at 07:19
0

Bit fields stored using the defined type like this.

int milliseconds : 10; // 4 bytes
BYTE seconds      : 6; // 1 byte
BYTE minutes      : 6; // 1 byte
BYTE hour         : 5; // 1 byte
BYTE day          : 5; // 1 byte

This is how MSVC 2013 x64 stores it in the memory

11111111 11000000 00000000 00000000
11111100 11111100 11111000 11111000

So, to sum up, you use 8 bytes to store one structure. 8x10 is 80 bytes. Everything is correct.

It's worth mentioning that gcc behaves differently and allows more tight packing.

Here the difference between gcc and MSVC implementation is explained: Forcing unaligned bitfield packing in MSVC

Community
  • 1
  • 1
SGrebenkin
  • 572
  • 4
  • 10
  • "Bit fields stored using the defined type like this" - is it documented somewhere? or is it a part of MS ABI perhaps? – Abyx Jan 22 '15 at 15:01
  • it doesn't say that compiler may not put `seconds` in the second byte. – Abyx Jan 22 '15 at 15:10
  • Because standard explicitly says that. The order is preserved – SGrebenkin Jan 22 '15 at 15:16
  • The standard doesn't specify the order. It's MS-specific, please read the article at the link you posted. But anyway, whether compiler may put `seconds` in the second byte or not - it has nothing to do with their order. – Abyx Jan 22 '15 at 15:32
  • C++14, 1.7.13: "Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object." That means the standard specifies the order. – SGrebenkin Jan 22 '15 at 15:45
  • bit-fields have no addresses. You might want to read the "Memory model" chapter, 1.7 [intro.memory] – Abyx Jan 22 '15 at 15:52
  • Oops it's 9.2.13 paragraph of the C++14 standard. – SGrebenkin Jan 22 '15 at 16:02
  • Bitfields have no addresses, agree. Anyway, it's worth reading http://stackoverflow.com/questions/1490092/c-c-force-bit-field-order-and-alignment , where it is stated that the order of bit fields is implementation dependent. For x64 implementation it is defined. Seconds won't be put in the second byte according to the example in msdn.microsoft.com/en-us/library/ewwyfdbe.aspx – SGrebenkin Jan 22 '15 at 16:09
  • That second example it about a zero-length bit-field. it's irrelevant here. – Abyx Jan 22 '15 at 16:35
  • Since it's implementation defined, we can refer to the actual memory layout of the given compiler - I've checked how Visual Studio does it, and it doesn't put 'second' field in the second byte. It puts it after the whole int. – SGrebenkin Jan 22 '15 at 17:22
  • but it it's not a part of ABI, then there is no guarantee that it won't be changed in next version of MSVC – Abyx Jan 22 '15 at 17:58