There are three basic approaches for extracting encoded information from a bitfield. The first two are related and differ only in the manner the bitfield struct is initialized. The first and shortest is to simply create a bitfield struct defining the bits associated with each member of the struct. The sum of the bits cannot exceed sizeof type * CHAR_BIT
bits for the type used to create the bitfield. A simple example is:
#include <stdio.h>
typedef struct {
unsigned cur : 10,
low : 10,
high : 10;
} temps;
int main (void) {
unsigned n = 0; /* encoded number of temps */
n = 58; /* fill number (58, 46, 73) */
n |= (46 << 10);
n |= (73 << 20);
temps t = *(temps *)&n; /* initialize struct t */
/* output value and temps */
printf ("\n number entered : %u\n\n", n);
printf (" %2hhu - %2hhu value : %u (deg. F)\n", 0, 9, t.cur);
printf (" %2hhu - %2hhu value : %u (deg. F)\n", 10, 19, t.low);
printf (" %2hhu - %2hhu value : %u (deg. F)\n\n", 20, 29, t.high);
return 0;
}
Note: memcpy
can also be used to initialize the value for the structure to avoid casting the address of n
. (that was done intentionally here to avoid inclusion of string.h
).
The next method involves creation of a union between the data type represented and the exact same struct discussed above. The benefit of using the union is that you avoid having to typecast, or memcpy
a value to initialize the struct. You simply assign the encoded value to the numeric type within the union. The same example using this method is:
#include <stdio.h>
typedef struct {
unsigned cur : 10,
low : 10,
high : 10;
} temps;
typedef union {
temps t;
unsigned n;
} utemps;
int main (void) {
unsigned n = 0; /* encoded number of temps */
n = 58; /* fill number (58, 46, 73) */
n |= (46 << 10);
n |= (73 << 20);
utemps u; /* declare/initialize union */
u.n = n;
/* output value and temps */
printf ("\n number entered : %u\n\n", n);
printf (" %2hhu - %2hhu value : %u (deg. F)\n", 0, 9, u.t.cur);
printf (" %2hhu - %2hhu value : %u (deg. F)\n", 10, 19, u.t.low);
printf (" %2hhu - %2hhu value : %u (deg. F)\n\n", 20, 29, u.t.high);
return 0;
}
Finally, the third method uses neither a struct or union and simply relies on bit shift operations to accomplish the same purpose. A quick example is:
#include <stdio.h>
#include <limits.h> /* for CHAR_BIT */
int main (void) {
unsigned n = 0; /* encoded number of temps */
unsigned char i = 0; /* loop counter */
unsigned char r = 10; /* number of bits in temps */
unsigned char s = 0; /* shift accumulator */
unsigned v = 0; /* extracted value */
n = 58; /* fill number (58, 46, 73) */
n |= (46 << 10);
n |= (73 << 20);
printf ("\n number entered : %u\n\n", n);
/* extract and output temps from n */
for (i = 0; i < (sizeof n * CHAR_BIT)/r; i++)
{
v = (n >> i * r) & 0x3ff;
printf (" %2hhu - %2hhu value : %u (deg. F)\n", s, s + r - 1, v);
s += r;
}
printf ("\n");
return 0;
}
Note: you can automate the creation of the mask
with the createMask
function. While longer, the shift method is not computationally intensive as shift operations take little to accomplish. While negligible, the multiplication could also be replaced with a shift and addition to further tweak performance. The only costly instruction is the division to set the loop test clause, but again it is negligible and all of these cases are likely to be optimized by the compiler.
All of the examples above produce exactly the same output:
$ ./bin/bit_extract_shift
number entered : 76593210
0 - 9 value : 58 (deg. F)
10 - 19 value : 46 (deg. F)
20 - 29 value : 73 (deg. F)