I need to identically interpret first 2 bits of first byte in memory (which came from the wire) with different C structs (network header descriptors). This is the minimal reproducible example:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
struct some {
uint8_t reserved : 6;
uint8_t flags : 2;
} __attribute__((packed));
struct some2 {
uint16_t b : 12;
uint16_t reserved : 2;
uint16_t flags : 2;
} __attribute__((packed));
int main(void)
{
char *ptr = malloc(8);
ptr[0] = 0xC0;
ptr[1] = 0x00;
struct some *hdr = (struct some *)ptr;
printf("%zu %u\n", sizeof(struct some), hdr->flags);
struct some2 *hdr2 = (struct some2 *)ptr;
printf("%zu %u\n", sizeof(struct some2), hdr2->flags);
return 0;
}
The output:
1 3
2 0
What is the reason of interpreting flags in the second struct as 0? Is there any error in struct some2 definition?
Order of fields when using a bit field in C postulates
C standard allows compiler to put bit-fields in any order. There is no reliable and portable way to determine the order.
But there is no putting through the bit-field mechanism from compiler code, this is just a byte from network, which I'm interpreting through the bit-field mechanism..
UPD
As Eric mentioned the one solution is to re-define struct such a way:
struct some2 {
uint8_t b1 : 4;
uint8_t reserved : 2;
uint8_t flags : 2;
uint8_t b2;
} __attribute__((packed));
But it I need to access the full b number of 12 bits, it will cause some ugly concatenation of 4-bit and 8-bit parts..
In
struct some, you have told the compiler to lay out a six-bit field and a two-bit field. Instruct some2, you have told the compiler to lay out a 12-bit field, a two-bit field, and another two-bit field.The compiler did so. For the first structure, it laid out the six-bit
reservedfield as bits 0-5 of the first (and only) byte, and it laid out the two-bitflagsfield as bits 6-7 of the byte. (Here, bits are numbered with 0 as the position with the least significant value when interpreted as a binary numeral and 7 as the most.)For the second structure, it laid out the 12-bit
bfield as bits 0-7 of the first byte and bits 0-3 of the second byte (equivalently, bits 0-11 of theuint16_tformed from the two bytes in little-endian order), the two-bitreservedfield as bits 4-5 of the second byte, and the two-bitflagsfield as bits 6-7 of the second byte.In
hdr->flags, you accessed one byte, which had been set to C016, using astruct sometype. This interpreted bits 6-7 of the byte as two-bitflagsfield, yielding 3.In
hdr2->flags, you accessed two bytes, the second of which had not been set to any value, using astruct some2type. This interpreted bits 6-7 of the second byte as the two-bitflagsfield. The byte apparently contained zeros in these bits, at least in effect, yielding 0.If you want to rely on your compiler’s layout of bit-fields then you need to define the structures differently, so that the
flagsfield ofstruct some2corresponds to bits 6-7 of the first byte. Given your compiler’s apparent behavior, you need to define a six-bit field (or fields totaling six bits) prior touint8_t flags : 2. That compels those six bits beforeflagsto be separate from a bit-field followingflags, so there is no way to define a single 12-bitbfield that includes both bits 0-5 from the first byte and additional bits from the second byte.Alternatively, you can access the byte using a character type and use the shift operator to extract bits 6-7.