Reputation: 11
How do you access a given 8 byte datatype in 4 bit increments using bit fields? How do I program this in C and C++?
Upvotes: 1
Views: 113
Reputation: 1821
Since @snb gave you a C++ solution, here is a C solution, using an array:
union split8bit_u {
uint8_t byte;
struct {
uint8_t l_nyb:4;
uint8_t u_nyb:4;
};
};
typedef union split8bit_u split8bit;
union split64bit_u {
uint64_t lword;
split8bit byte[8];
};
typedef union split64bit_u split64bit;
int main() {
// note that 0x0123456789ABCDEF
split64bit data;
data.lword = 0x0123456789ABCDEF;
for ( int i = 0; i < sizeof(data); ++i) {
printf("%X.%X.",data.byte[i].l_nyb,data.byte[i].u_nyb);
}
printf("\b \n");
// Prints "F.E.D.C.B.A.9.8.7.6.5.4.3.2.1.0"
}
This prints out the 16 discrete 4 bit values.
Upvotes: 0
Reputation: 4892
To preface, I'm going to show you how to do this in C++
While it seems like you should be able to do this with just bitfields, since bitfields have to be initialized element wise you cannot use bitfields. See more here on how to use bitfields in c++. To see why attempt to do the following:
struct S{
std::uint32_t x : 16;
std::uint32_t y : 16;
}
// 65536 = 0001 0000 0000 0000 0000 in binary
S s = {65536};
std::cout << s.x << std::endl;
std::cout << s.y << std::endl;
// >>> 0
// >>> 0
// since the first parameter actually only initializes x, and not y.
//you also cannot do the following
S s = 65536 // Error! no conversion between S and uint32_t!
Wow, how do I even set the base type to the bit field then! To solve that problem we need to use unions. I'll show you the simplest answer to solve this with unions, then link to a more complicated, but compact solution using anonymous unions and structures.
This is essentially what you would need to do for your problem:
struct nibble64bitField{
uint64_t A:4;
uint64_t B:4;
uint64_t C:4;
uint64_t D:4;
uint64_t E:4;
uint64_t F:4;
uint64_t G:4;
uint64_t H:4;
uint64_t I:4;
uint64_t J:4;
uint64_t K:4;
uint64_t L:4;
uint64_t M:4;
uint64_t N:4;
uint64_t O:4;
uint64_t P:4;
};
union split8bytes{
std::uint64_t integer;
nibble64bitField bitfield;
};
int main() {
// note this is 255 + 256, or 0000 1111 1111 + 0001 0000 0000
uint64_t eightbyte_value = 511;
split8bytes split = {eightbyte_value};
std::cout << split.bitfield.A << " should be 15" << std::endl;
std::cout << split.bitfield.B << " should be 15" <<std::endl;
std::cout << split.bitfield.C << " should be 1" << std::endl;
std::cout << split.bitfield.D << " should be 0" << std::endl;
std::cout << split.integer << " should be 511" << std::endl;
return 0;
}
Here you use the use of unions. Now why did we use unions here? Unions allow you to refer to the given union as any member within the union, but only takes up as much space as the largest byte size member. This allows us to set splitbytes
to an integer value, but access individual nibbles (groups of 4 bits or half bytes) from that integer.
As promised, another user attempted to do the same as you but over in code review. Thier solution was to do this conversion manually, and another user offered to do the conversion using the union struct paradigm (not the same question, it is not a duplicate). Here anonymous structures were used with compiler pragmas in order to ensure packed-ness (but only on some compilers)
Upvotes: 1