1

I have a struct that looks something like the following:

typedef unsigned __int16 UINT16;
typedef unsigned __int64 UINT64;

struct Example {
    struct {
        UINT64 var1 : 5;
        UINT64 var2 : 2;
        UINT64 var3 : 29;
        UINT64 var4 : 23;
        UINT64      : 5;
    };
    struct {
        UINT16 var5 : 4;
        UINT16 var6 : 2;
        UINT16      : 10;
    };
};

I was expecting sizeof(struct Example) to return 10, but it returned 16. I have no clue why this is happening, and I would appreciate any input on the matter.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
janovak
  • 1,531
  • 2
  • 12
  • 22
  • 4
    very related: http://stackoverflow.com/questions/38335882/why-class-size-increases-when-int64-t-changes-to-int32-t – NathanOliver Jul 18 '16 at 18:44
  • 5
    Probably alignment. If you have two `Example` structs in an array, they probably need to be aligned on an 8-byte boundary (because of the `UINT64`). In order to ensure the second struct is properly aligned, the previous struct needs to have 6 bytes of padding at the end. Hence, the size is 16. – Cornstalks Jul 18 '16 at 18:47
  • 1
    You can use `#pragma pack` or `__attribute((packed))` to remove the padding. But you should do a little research about structure packing before making that decision. That is, search for `[c] pragma pack` and `[c] __attribute packed` here on SO, and read some of the Q&A's about packing. – user3386109 Jul 18 '16 at 18:54
  • See also http://stackoverflow.com/questions/7822798/when-should-we-not-use-pragma-pack – cdarke Jul 18 '16 at 18:54
  • Note: "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." Using `UINT64`, `UINT16`, is not portable. – chux - Reinstate Monica Jul 18 '16 at 18:59
  • 1
    @chux It really depends if he is using C or C++. In C++ it is fine. – NathanOliver Jul 18 '16 at 19:09
  • 1
    @NathanOliver Did not notice C++ tag. LSNED. Mutter: curse these dual language posts. – chux - Reinstate Monica Jul 18 '16 at 19:13
  • 1
    Don't use homebrew names/defines for types C does provide by default. Use fixed width types from `stdint.h`! And don't add tags for different languages! – too honest for this site Jul 18 '16 at 19:21
  • @Olaf - you're assuming `stdint.h` types are available. Different compilers and different versions of standards make that a moving target. Even dropping the C++ tag makes a difference. – jww Jul 19 '16 at 02:57
  • @jww: Please read 7.20.1.1p3. Briefly: An implementation **has to** provide `int16_t` etc. if it does have such types. And there is exactly one valid version of the C standard. Without additional information, we have to assume a compliant implementation. (Let apart that ancient compilers which lag behind >17 years will result in other problems, too.) – too honest for this site Jul 19 '16 at 12:14
  • @Olaf - I don't assume the latest compiler from a particular vendor. The Stack Overflow tags are not set-up that way either. A user signals their interest in a particular version or vendor by using a specific tag, not a generic one. – jww Jul 19 '16 at 12:18
  • @jww: The standard tags imply the standard version which would be C11, possibly C99 (as C11 mostly added features, it definitively did not change **this** feature). IIRC, there is a meta about this subject. Feel free to search, this is not the place. Anyway, every modern compiler does support C99 at least. If you prefer using an ancient and partially incompatible version, you still should no propagate this here. Tempora mutantur ... – too honest for this site Jul 19 '16 at 13:04
  • @Olaf - Perhaps you should review [What are tags, and how should I use them?](https://stackoverflow.com/help/tagging) As far as I know, that's the site's official policy. If ***`C`*** were ***`C11`***, then it would be a synonym and it would be stated in the tag description; similar applies to C++. You also seem to have a novel use for tags: you edit tags to suit your comments and answers. Perhaps you should ask about that on Meta. – jww Jul 19 '16 at 13:11
  • @jww: To repeat: there is only one C standard. With the release of a new version, the proviaous version has been withdrawn (see the foreword!). Then the description of the tag clearly states that "This tag is for questions related to C ..." and "The language is standardised as ISO 9899." (Again: ther is only **one** ISO9899 which currently is :2011. – too honest for this site Jul 19 '16 at 14:17
  • @Olaf - and to repeat: you are wrong. There are multiple version of the C and C++ standard. There are past versions, current versions and future versions. As far as I now, no one has ever provided a completely conforming implementation, so there are even vendor's flavors of the standard, like GNU's, LLVM's and MS. – jww Jul 19 '16 at 21:47

1 Answers1

6

This is due to alignment of the fields.

The first internal struct uses a uint64_t as the underlying type for the bitfield, while the second uses a uint16_t as the underlying type.

In order for the struct as a whole to be properly aligned, it must be aligned at the same offset as that required by the largest "base" internal field, which in this case is a uint64_t and therefore requires 8 byte alignment.

Without such alignment, if an array of this type is created then not all array elements will start on an 8 byte offset. So 6 padding bytes are added at the end to ensure proper alignment.

dbush
  • 205,898
  • 23
  • 218
  • 273