I wouldn't do it this way because:
- Bit field packing is implementation-defined. See C99 §6.7.2.1p10: "The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined"
- This violates strict aliasing rules.
What actually happens is most likely in this case i
is aligned on 4 bytes (the size of int
).
You can disable alignment and it should give you a better result:
#pragma pack(1)
struct test{
unsigned int a : 16;
unsigned int b : 16;
unsigned int c : 16;
unsigned int d : 16;
unsigned int e : 16;
unsigned int f : 16;
unsigned int g : 16;
unsigned int h : 8;
unsigned int i : 16;
};
#pragma pack()
int main(int argc, char *argv[]){
//bytes 0, 1, 15, 16 are all equal to 0x31
unsigned char *data = "1134567890123451189";
struct test *testStructure = (struct test*) data;
printf("%X %X\n", testStructure->a, testStructure->i);
return 0;
}
On clang, x86_64 it prints:
3131 3131
However, this code is still illegal, it is not guaranteed to work this way everywhere.
To solve the bitfield issue try not to use bitfields (fortunately in your particular case it's possible). But there is unfortunately no easy solution to the aliasing problem; most people who rely on type punning simply compile with -fno-strict-aliasing
(including Linux kernel guys). Others jump through the hoops using union
s, which strictly speaking is still illegal but is common idiom and is well-supported by most compilers:
#include <stdio.h>
#include <stdint.h>
#pragma pack(1)
struct test{
uint16_t a;
uint16_t b;
uint16_t c;
uint16_t d;
uint16_t e;
uint16_t f;
uint16_t g;
uint8_t h;
uint16_t i;
};
union u{
struct test t;
char str[17];
};
#pragma pack()
int main(int argc, char *argv[]){
//bytes 0, 1, 15, 16 are all equal to 0x31
char *data = "1134567890123451189";
union u *testStructure = (union u*) data;
printf("%X %X\n", testStructure->t.a, testStructure->t.i);
return 0;
}