0

I was recently introduced to bit fields. I have a following union.

 typedef struct
  {
            uint16_t        var1:16;         
            uint32_t       var2:28;         
            uint8_t        var3:8;       
            uint8_t        var4:8;     
            uint8_t        var5:8;   
            uint8_t        var6:8;  
            bool        var7:1;  
            bool         var8:1;          
            bool         var9:1;    
            bool         var10:1;  
            bool         var11:1;    
            uint8_t         var12:1;    
            uint8_t        var13:7;        
            uint8_t        var14:7;   
            uint32_t       var15:18;           
            uint16_t       var16:10;         
            uint8_t        var17:4;         
  } packet_bit_map;
typedef union
{

packet_bit_map packetsArrived ;
  uint8_t packetRaw[16];
} packetDecode;

the idea is to copy the data stream coming from external device which is 16 bytes individual byte values and then use the bit fielded structure to access particular information. But I was not able to do it after debugging I found out that the size of union packetDecode ended up being 20 and not 16 as expected because packetsArrived is holding 20bytes . Why is this happening? and how to avoid this padding?

EDIT: I know one solution that is to use attribute padding but sadly i cannot use inline function in my project.

timrau
  • 22,578
  • 4
  • 51
  • 64
  • http://jkz.wtf/bit-field-packing-in-gcc-and-clang – KamilCuk Sep 09 '21 at 07:35
  • 1
    TL;DR: "How do bit fields actually work?" Poorly. "and in what case bit padding happens?" Nobody knows, because it isn't standardized. "I was recently introduced to bit fields" By whom? By some trustworthy C source or a random "friendly" person? – Lundin Sep 09 '21 at 07:39
  • I wrote an answer a while ago about [why bitfields should not be used for serialization](https://stackoverflow.com/a/55825057/694733). I think most of it applies to your situation. – user694733 Sep 09 '21 at 07:50
  • There's also this: https://stackoverflow.com/questions/6043483/why-bit-endianness-is-an-issue-in-bitfields/6044223#6044223 – Lundin Sep 09 '21 at 08:24
  • Even if you get it packed in 16 bytes, there is no guarantee that the structure will match the source. - the order of the packing is also implementation defined. It is not a good idea to use bitfields for this purpose. There are few good reasons to use bitfields, and no way to make it portable. – Clifford Sep 09 '21 at 11:52
  • What would inline functions have to do with attribute padding? Anyway, what is usually used especially when not used just internally, is to use just byte arrays and a (de-)serializer, that can cope with byte and bit orders and alignment. – kesselhaus Sep 13 '21 at 08:07

3 Answers3

3

If you want to have it packed:

  1. Group fields into types they fully fit.
  2. Do not use bool as the type of the bitfield.
  3. pack the structure (it is implementation dependant)
 typedef struct __attribute__((packed))
  {
            uint16_t        var1:16;         
            uint64_t        var2:28;         
            uint64_t        var3:8;       
            uint64_t        var4:8;     
            uint64_t        var5:8;   
            uint64_t        var6:8;  
            uint64_t        var7:1;  
            uint64_t        var8:1;          
            uint64_t        var9:1;    
            uint64_t        var10:1;  
            uint16_t        var11:1;    
            uint16_t        var12:1;    
            uint16_t        var13:7;        
            uint16_t        var14:7;   
            uint32_t        var15:18;           
            uint32_t        var16:10;         
            uint32_t        var17:4;         
  } packet_bit_map;

https://godbolt.org/z/Pz3YzEE76

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Actually the C standard explicitly says that bitfields can hold bool, though it unhelpfully doesn't mention how large such bool members will be or how they will be packed. It says "a _Bool bit-field has the semantics of a _Bool" which is basically a no s**t Sherlock statement. We still don't know if bool is 1 bit or 8 bits. And then "a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit". In theory bools should be packed. But it isn't mandatory and the C standard allows compilers to make completely irrational implementations. – Lundin Sep 09 '21 at 07:44
  • @Lundin if you have 1 bit unsigned bitfield what is the point of `bool`? – 0___________ Sep 09 '21 at 07:56
  • The C standard doesn't mention 1 bit bitfields. It just unhelpfully 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 type picked means that the compiler must reserve "an addressable storage unit" which is some vague mumbo-jumbo term only used together with bit-fields, basically the amount of memory actually allocated. – Lundin Sep 09 '21 at 08:21
0

Consider using the #pragma pack on the struct declaration of packet_bit_map. The usage and effect of #pragma pack is explained very good in the answer of this post: #pragma pack effect

HooNose
  • 179
  • 7
  • See point 3 in @0___________'s answer. The question you have linked to applies to Microsoft's compiler, and this question is tagged [embedded]. `#pragma` directives are _always_ implementation defined, and unrecognised directives are simply ignored. Also the answer does not explicitly refer to bit-fields. Packing and and ordering of bitfields is in any case implementation defined, so packing alone may not achieve the required behaviour. – Clifford Sep 09 '21 at 11:49
  • Ok, I see. Thank you for the clarification. My thought doesn't actually apply to the specific compiler in question. – HooNose Sep 09 '21 at 16:10
0
6.7.2.1 Structure and union specifiers
...
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 implementation-defined whether atomic types are permitted.
...
11     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. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
C 2011 Online Draft

In English, what this is saying is that it's up to the implementation to map bitfields to storage, and whether bitfields are immediately adjacent to each other or whether there are gaps. Looking at your code, var1 takes up 16 bits (two full 8-bit bytes), var2 takes up 28 bits (three and a half 8-bit bytes), and var3 takes up 8 bits. A reasonable mapping would allocate a 2-byte storage unit for var1 and a 4-byte storage unit for var2, along with an additional storage unit to hold all or part of var3. So the question is whether var3 spans two bytes, or whether it gets its own byte:

+----- var1 -----+---------- var2 ------------+- var3 -+
|                |                            |        |        span
+----------------+----------------------------+----+--------+
|                |                                 |        |
+----------------+----------------------------+----+--------+
|                |                            |    |        |   no span                               
+----- var1 -----+---------- var2 ------------+    +- var3 -+

It's up to the implementation to decide which method to use. Given that var3 through var6 are all 8 bits, I'd expect the implementation to allocate another 4-byte storage unit and map all four of those members to it, leaving the 4-bit gap between var2 and var3. So yes, the resulting struct type takes up more bytes than the sum of the bits would suggest.

John Bode
  • 119,563
  • 19
  • 122
  • 198