2

I have a struct that is supposed to be 8 byte in size.

struct Slot {
    uint8_t T;
    uint8_t S;
    uint32_t O : 24;
    uint32_t L : 24;
}

However, sizeof(Slot) tells me the size is 12 byte. So the compiler seems to pad the data although it shouldn't be necessary (probably because the 24-bit cannot be aligned properly).

A hacky solution would be to use 3 one-byte fields instead of a single three-byte field:

struct Slot2 {
    uint8_t T;
    uint8_t S;
    uint8_t O1;
    uint8_t O2;
    uint8_t O3;
    uint8_t L1;
    uint8_t L2;
    uint8_t L3;
}; // sizeof(Slot2) = 8

Is there any other way to achieve this?

mschrimpf
  • 539
  • 2
  • 8
  • 19

7 Answers7

3

This gives size 8 bytes on MSVC without packing pragma.

struct Slot {
    uint32_t O : 24;
    uint32_t T : 8;
    uint32_t L : 24;
    uint32_t S : 8;
};
Weather Vane
  • 33,872
  • 7
  • 36
  • 56
3

There is no way anyone can tell what your code will do or how the data will end up in memory, because the behavior of bit fields is poorly specified by the C standard. See this.

  • It is not specified what will happen when you use an uint32_t for a bit field.
  • You can't know if there will be padding bits.
  • You can't know if there will be padding bytes.
  • You can't know where padding bits or bytes will end up.
  • You can't know whether 8 bits of the 2nd 24 bit chunk end up immediately after previous data, or if it is aligned to the next 32 bit segment.
  • You can't know which bit that is msb and which that is lsb.
  • Endianess will cause problems.

The solution is to not use bit fields at all. Use the bitwise operators instead.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
2

Your "hack" solution is exactly the right one. I suspect that the layout is determined by some outside factors, so you won't be able to map this to a struct in any better way. I suspect the order of bytes in your 24 bit numbers is also determined by the outside, and not by your compiler.

To handle that kind of situation, a struct of bytes or just an array of bytes is the easiest and portable solution.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
0

I think, what you want, 8 bytes, is not something that the C standard can gurantee, with your first definition.

Related: from C11 standard, Chapter §6.7.2.1,

An implementation may allocate any addressable storage unit large enough to hold a bitfield. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined.

You can have a way out however, if you can adjust the variable so that they can fit properly in 32-bit alignment, then

24 + 8 + 24 + 8 = 64 bits = 8 bytes.

you can have a structure of size 8 bytes.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • This is just one of many things related to bit fields that the standard can't guarantee. – Lundin May 13 '15 at 11:34
  • 2
    @Lundin Sir, I did not get you. What I understood from the standard is like, for `uint32_t`, two bitfields of `:24` and `:8` will be packed into a single `uint32_t`. Please correct me if i'm missing something. – Sourav Ghosh May 13 '15 at 11:36
  • 1
    Regarding uint32_t: 6.7.2.1/5 `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. ` It is not specified what will happen if you use any other type than the ones mentioned. If we ignore that and assume that uint32_t and int are the same size on the given system, then indeed the compiler must pack them together, because they fit, as stated by the text you cite. But there is also numerous other poorly-defined issues that may cause problems here, such as struct padding bytes. – Lundin May 13 '15 at 11:42
0

With this compiler dependant solution (works with gcc, msvc) the struct will be 8 bytes:

#pragma pack(push, 1)
struct Slot {
    uint8_t T;
    uint8_t S;
    uint32_t O : 24;
    uint32_t L : 24;
};
#pragma pack(pop)

This will set the alignment of the struct to 1 byte.

simon
  • 1,210
  • 12
  • 26
0

On MSVC the following works and keeps your variable orders the same:

struct Slot {
    uint64_t T : 8;
    uint64_t S : 8;
    uint64_t O : 24;
    uint64_t L : 24;
};

This is not guaranteed across compilers though. YMMV on other compilers.

Mike Vine
  • 9,468
  • 25
  • 44
-3

Try something like as shown below:

struct Slot {
    uint32_t O : 24;
    uint8_t T;
    uint32_t L : 24;
    uint8_t S;
};
Nipun Talukdar
  • 4,975
  • 6
  • 30
  • 42
  • Not sure it is guaranty to have `sizeof(Slot) == 8` (even if gcc give 8 [Demo](https://ideone.com/MrtHWQ)) – Jarod42 May 13 '15 at 11:27
  • That's even bigger on my MSVC: 16 bytes – Weather Vane May 13 '15 at 11:28
  • Thats what I actually would expect fro any c++11 compliant compiler: O and T have to be separate memory locations, so I don't see, how they can be legally packed into a single 4-Byte memory location. @Jarod: did you Test with -std=c++11? – MikeMB May 13 '15 at 11:31
  • 1
    If you want bit fields, make everything bit fields. uint32_t T: 8. What you do is actually _guaranteed_ to not fit into 8 bytes, because a non-bit field after a bit field _must_ start with a new storage unit. – gnasher729 May 13 '15 at 11:32
  • @MikeMB: yes, even with clang [Demo](http://coliru.stacked-crooked.com/a/0bebe97ce90b7cc5) (I also expected 16 bytes). – Jarod42 May 13 '15 at 11:39
  • Dear downvoters, can you please explain why did you downvote the answer? – Nipun Talukdar May 13 '15 at 12:05
  • Thats interesting, but as almost anything around bitfields is implementation defined, on should probably not be suprised – MikeMB May 13 '15 at 12:36