How to do Endian conversion when dealing with bit fields - c

I am dealing with a situation where I need to send/receive data via a TCP/IP socket between myself (client) and a server. The message structure is proprietary, but is basically arrays of uint32_t. I am tasked with handling the Endian conversion on my end. As the client, I am operating in Windows (little endian). The server is operating in VxWorks environment (big endian). Therefor, I need to convert data I send from little to big, and data I receive from big to little.
Now, I am aware that endianness refers to BYTE order within a word. So, I created a function that would do the byte swapping for each uint32_t word in a given array. See below.
void convertEndian(uint32_t inputData[], int size)
{
uint32_t b1, b2, b3, b4;
for (int i = 0; i < size; ++i)
{
b1 = (inputData[i] & 0xFF000000) >> 24;
b2 = (inputData[i] & 0x00FF0000) >> 8;
b3 = (inputData[i] & 0x0000FF00) << 8;
b4 = (inputData[i] & 0x000000FF) << 24;
inputData[i] = b1 | b2 | b3 | b4;
}
}
This approach is fine for certain message types I'll be dealing with, where each word is defined by an entire uint32_t value. However, some messages have many words that have their own unique bit fields. Below is an example of one:
Struct test
{
Unsigned int var1 : 16;
Unsigned int var2 : 12;
Unsigned int var3 : 1;
Unsigned int var4 : 1;
Unsigned int var5 : 1;
Unsigned int var6 : 1;
}
How do I implement endian conversion for such cases? There is one message type for example, where I will be receiving an array of about 32 words of uint32_t and each of those words has its own set of bit fields representing various things.
I guess my only choice is to mask/shift for each word as needed. But then I will pretty much have to make 32 unique functions for each word. It seems very labor intensive.
Any suggestions would be appreciated.

This is hopefully without too many assumptions.
Totally untested, humour not marked.
static_assert(sizeof(test) == sizeof (uint32_t), "Houston we have a problem");
template < typename NetOrHost>
void convertEndian(uint32_t inputData[], int size) {
for (int i = 0; i < size; ++i)
inputData[i] = NetOrHost(inputData[i]);
}
// or simply use
std::for_each(inputData, inputData+size, htonl /* ntohl for the other way */); // this part needs a bit of testing ...
// Now encoding the test struct
// Using https://stackoverflow.com/a/20194422/4013258 BitCount
// or just use magic values, *hands OP a foot-gun*
// Once only
std::array<int, 6> bits; // todo make this constexpr
bits[0] = BitCount(test.var1);
bits[1] = BitCount(test.var2);
etc ...
static_assert(std::accumulate(bits.begin(), bits.end(), 0) == 32, "Preconditions violated");
// this goes well because we know that it fits in a 32-bit unsigned integer.
uint32_t encoded = 0;
encoded = test.var1;
encoded <<= bits[1];
encoded += test.var2;
encoded <<= bits[2];
encoded += test.var3;
etc.
// To decode
uint32_t decode = inputData[i];
test.var6 = decode & ((1 << bits[5])-1); // or make a mask array
decode >>= bits[5];
etc.
This would be a lot easier with reflection ...

Related

QR-encoding with Bit Fields

I am trying to perform an exercise in C language whose scope is to encode a message in a 2Q QR-Code via Byte method. This means that a given string should be encoded in a message with:
4 bits for the Mode Indicator (0100 in my case);
8 bits for the length of the message;
the message itself (20 characters);
4 null bits as End Of Message signal;
16 bits for 0xEC11 Padding.
I tried using a struct with bit fields like in the folloowing code, but it didn't work out, because the bit order can't be forced.
typedef struct
{
unsigned char mode : 4;
unsigned int length : 8;
unsigned char *message;
unsigned char eof : 4;
unsigned int padding : 16;
} code;
I also tried to left shift the bits of the coded message, but once again I received an error message "int value expected", which means (if I understood correctly) I can't shift a struct.
Could anybody suggest an elegant way of performing this task?
I eventually implemented it with data in uint8_t and bit shift.
It means I saved the string in an array and than shifted it like in the following:
for(int n_digit = 0; n_digit <= length; n_digit++)
{
if(n_digit < length)
{
*(code + n_digit) = (*(code + n_digit) << 4) + (*(code + n_digit + 1) >> 4);
}
else if (n_digit == length)
{
*(code + n_digit) = *(code + n_digit) << 4;
}
}
Perhaps not the highest quality code, but it works just fine.

sending integer greater than 255 with uint8 array

I want to send integer greater than 255 using uint8 array from mobile to Arduino over bluetooth.
Since BLE module that I'm using does not accept Uint16Array, I'm restricted to use Uint8 array only.
My App code :
var data = new Uint8Array(3);
data[0]= 1;
data[1]= 123;
data[2]= 555;
ble.writeWithoutResponse(app.connectedPeripheral.id, SERVICE_UUID, WRITE_UUID, data.buffer, success, failure);
My Device Specific Code :
void SimbleeBLE_onReceive(char *data, int len) {
Serial.print(data[0]); // prints 1
Serial.print(data[1]); // prints 123
Serial.print(data[2]); // failed to print 555
}
Since uint8 only allows integer upto 255, How do I send greater values than that ?
You have to split it. You already know (or you should) that an int16 has, well, 16 bits (so it takes two bytes to store it).
Now very small digression about endianness. With endianness you mean the order of the bytes when stored. For instance, if you have the value 0x1234, you can either store it as 0x12 0x34 (big endian) or as 0x34 0x12 (little endian).
I don't know what language you use, so... Normally in C++ you do something like this:
const int datalen = 3;
uint16_t data[datalen];
data[0]= 1;
data[1]= 123;
data[2]= 555;
uint8_t sendingData[] = new uint8_t[datalen * sizeof(uint16_t)];
for (int i = 0; i < datalen; i++)
{
sendingData[i * 2] = (data[i] >> 8) & 0xFF;
sendingData[i * 2 + 1] = data[i] & 0xFF;
}
functionToSendData(sendingData, datalen * sizeof(uint16_t));
This sends in big endian format. If you prefer the little endian one, write
sendingData[i * 2] = data[i] & 0xFF;
sendingData[i * 2 + 1] = (data[i] >> 8) & 0xFF;
A simpler version can be
const int datalen = 3;
uint16_t data[datalen];
data[0]= 1;
data[1]= 123;
data[2]= 555;
functionToSendData((uint8_t*)data, datalen * sizeof(uint16_t));
In the first case you know the endianness of the transmission (it is little or big according to how you code), in the second it depends on the architecture and/or the compiler.
In JavaScript you can use this:
var sendingData = new Uint8Array(data.buffer)
and then send this new array. Credits go to this answer
When you receive it, you will have to do one of these three things to convert it
// Data is big endian
void SimbleeBLE_onReceive(char *receivedData, int len) {
uint16_t data[] = new uint16_t[len/2];
for (int i = 0; i < len/2; i++)
data = (((uint16_t)receivedData[i * 2]) << 8) + receivedData[i * 2 + 1];
Serial.print(data[0]);
Serial.print(data[1]);
Serial.print(data[2]);
}
// Data is little endian
void SimbleeBLE_onReceive(char *receivedData, int len) {
uint16_t data[] = new uint16_t[len/2];
for (int i = 0; i < len/2; i++)
data = receivedData[i * 2] + (((uint16_t)receivedData[i * 2 + 1]) << 8);
Serial.print(data[0]);
Serial.print(data[1]);
Serial.print(data[2]);
}
// Trust the compiler
void SimbleeBLE_onReceive(char *receivedData, int len) {
uint16_t *data = receivedData;
Serial.print(data[0]);
Serial.print(data[1]);
Serial.print(data[2]);
}
The last method is the most error-prone, since you have to know what endianness uses the compiler and it has to match the sendong one.
If the endianness mismatches you will receive what you think are "random" numbers. It is really easily debugged, though. For instance, you send the value 156 (hexadecimal 0x9C), and receive the 39936 (hexadecimal 0x9C00). See? The bytes are inverted. Another example: sending 8942 (hex 0x22EE) and receiving 60962 (hex 0xEE22).
Just to finish, I think you are going to have problems with this, because sometimes you will not receive the bytes "in one block", but separated. For instance, when you send 1 123 555 (in hex and, for instance, big endian this will be six bytes, particularly 00 01 00 7B 02 2B) you may get a call to SimbleeBLE_onReceive with just 3 or 4 bytes, then receive the others. So you will have to define a sort of protocol to mark the start and/or end of the packet, and accumulate the bytes in a buffer until ready to process them all.
Use some form of scaling on the data at sending and receiving ends to keep it within the 0 to 255 range. for example dividing and multiplying so the data is sent below 255 and then scale it up at the receiving end.
Another way, if you know the hi and low range of the data would be to use the Arduino mapping function.
y = map(mapped value, actual lo, actual hi, mapped lo, mapped hi)
If you don't know the full range, you could use the constrain() function.

endianness conversion, regardless of endianness

Many implementations of htonl() or ntohl() test for the endianness of the platform first and then return a function which is either a no-op or a byte-swap.
I once read a page on the web about a few tricks to handle to/from big/little-endian conversions, without any preconceived knowledge of the hardware configuration. Just taking endianness for what it is : a representation of integers in memory. But I could not find it again, so I wrote this :
typedef union {
uint8_t b[4];
uint32_t i;
} swap32_T;
uint32_t to_big_endian(uint32_t x) {
/* convert to big endian, whatever the endianness of the platform */
swap32_T y;
y.b[0] = (x & 0xFF000000) >> 24;
y.b[1] = (x & 0x00FF0000) >> 16;
y.b[2] = (x & 0x0000FF00) >> 8;
y.b[3] = (x & 0x000000FF);
return y.i;
}
My two questions are :
Do you know a cleaner way to write this to_big_endian() function ?
Did you ever bookmarked this mysterious page I can not find, which contained very precious (because unusual) advices on endianness ?
edit
not really a duplicate (even if very close) mainly because I do not want to detect endianness. The same code compile on both architecture, with the same result
little endian
for u = 0x12345678 (stored as 0x78 0x56 0x34 0x12)
to_big_endian(u) = 0x12345678 (stored as 0x78 0x56 0x34 0x12)
big endian
for u = 0x12345678 (stored as 0x12 0x34 0x56 0x78)
to_big_endian(u) = 0x78563412 (stored as 0x78 0x56 0x34 0x12)
same code, same result... in memory.
Here is my own version of the same (although memory convention in this example is little endian instead of big endian) :
/* unoptimized version; solves endianess & alignment issues */
static U32 readLE32 (const BYTE* srcPtr)
{
U32 value32 = srcPtr[0];
value32 += (srcPtr[1]<<8);
value32 += (srcPtr[2]<<16);
value32 += (srcPtr[3]<<24);
return value32;
}
static void writeLE32 (BYTE* dstPtr, U32 value32)
{
dstPtr[0] = (BYTE)value32;
dstPtr[1] = (BYTE)(value32 >> 8);
dstPtr[2] = (BYTE)(value32 >> 16);
dstPtr[3] = (BYTE)(value32 >> 24);
}
Basically, what's missing in your function prototype to make the code a bit easier to read is a pointer to the source or destination memory.
Depending on your intentions, this may or may not be an answer to your question. However, if all you want to do is to be able to convert various types to various endiannesses (including 64-bit types and little endian conversions, which the htonl obviously won't do), you may want to consider the htobe32 and related functions:
uint16_t htobe16(uint16_t host_16bits);
uint16_t htole16(uint16_t host_16bits);
uint16_t be16toh(uint16_t big_endian_16bits);
uint16_t le16toh(uint16_t little_endian_16bits);
uint32_t htobe32(uint32_t host_32bits);
uint32_t htole32(uint32_t host_32bits);
uint32_t be32toh(uint32_t big_endian_32bits);
uint32_t le32toh(uint32_t little_endian_32bits);
uint64_t htobe64(uint64_t host_64bits);
uint64_t htole64(uint64_t host_64bits);
uint64_t be64toh(uint64_t big_endian_64bits);
uint64_t le64toh(uint64_t little_endian_64bits);
These functions are technically non-standard, but they appear to be present on most Unices.
It should also be said, however, as Paul R rightly points out in the comments, that there is no runtime test of endianness. The endianness is a fixed feature of a given ABI, so it is always a constant at compile-time.
Well ... That's certainly a workable solution, but I don't understand why you'd use a union. If you want an array of bytes, why not just have an array of bytes as an output pointer argument?
void uint32_to_big_endian(uint8_t *out, uint32_t x)
{
out[0] = (x >> 24) & 0xff;
out[1] = (x >> 16) & 0xff;
out[2] = (x >> 8) & 0xff;
out[3] = x & 0xff;
}
Also, it's often better code-wise to shift first, and mask later. It calls for smaller mask literals, which is often better for the code generator.
Well, here's my solution for a general signed/unsigned integer, independent of machine endianness, and of any size capable to store the data ---you need a version for each, but the algorithm is the same):
AnyLargeEnoughInt fromBE(BYTE *p, size_t n)
{
AnyLargeEnoughInt res = 0;
while (n--) {
res <<= 8;
res |= *p++;
} /* for */
return res;
} /* net2host */
void toBE(BYTE *p, size_t n, AnyLargeEnoughInt val)
{
p += n;
while (n--) {
*--p = val & 0xff;
val >>= 8;
} /* for */
} /* host2net */
AnyLargeEnoughInt fromLE(BYTE *p, size_t n)
{
p += n;
AnyLargeEnoughInt res = 0;
for (n--) {
res <<= 8;
res |= *--p;
} /* for */
return res;
} /* net2host */
void toLE(BYTE *p, size_t n, AnyLargeEnoughInt val)
{
while (n--) {
*p++ = val & 0xff;
val >>= 8;
} /* for */
} /* host2net */

Bitwise Operations in a Steganography Program (C)

I'm relatively new to both C and bitwise operations and I'm having trouble with an assignment I've been given in my class. A majority of the code has been given to me, but I've been having issues figuring out a part pertaining to bitwise operations. Once I figure this part out, I'll be golden. I hope that someone can help!
Here is the excerpt from my assignment:
You will need to use 8 bytes of an image to hide one byte of information (remember that only LSBs of the cover image can be modified). You will use the rest of the 16 bytes of the cover image to embed 16 bits of b.size (two least significant bytes of the size field for the binary data), next 32 bytes of the cover will be used to embed the file extension
for the payload file, and after that you will use 8*b.size bytes to embed the payload (b.data).
What this program is doing is stenography of an image, and I have to modify the least significant bits of the image read in using data from a file that I created. Like I said, all of the code for that is already written. I just can't figure out how to modify the LSBs. Any help would be greatly appreciated!!!
The functions I have to use for reformatting the LSBs are as follows:
byte getlsbs(byte *b);
void setlsbs(byte *b, byte b0);
This is what I've attempted thus far:
/* In main function */
b0 = getlsbs(&img.gray[0])
/* Passing arguments */
byte getlsbs(byte *b)
{
byte b0;
b0[0] = b >> 8;
return b0;
}
I'm honestly at a complete loss. I've been working on this all night and I still barely have made headway.
To set LSB of b to 1:
b |= 1;
To set LSB of b to 0:
b &= 0xFE;
Here is an idea how the functions could be implemented. This code is not tested.
byte getlsbs(byte *b)
{
byte result = 0;
for (int i = 0; i < 8; ++i)
{
result >>= 1;
if (*b & 1)
result |= 0x80;
++b;
}
return result;
}
void setlsbs(byte *b, byte b0)
{
for (int i = 0; i < 8; ++i)
{
if (b0 & 1)
*b |= 1;
else
*b &= 0xFE;
++b;
b0 >>= 1;
}
}

How do I extract specific 'n' bits of a 32-bit unsigned integer in C?

Could anyone tell me as to how to extract 'n' specific bits from a 32-bit unsigned integer in C.
For example, say I want the first 17 bits of the 32-bit value; what is it that I should do?
I presume I am supposed to use the modulus operator and I tried it and was able to get the last 8 bits and last 16 bits as
unsigned last8bitsvalue=(32 bit integer) % 16
unsigned last16bitsvalue=(32 bit integer) % 32
Is this correct? Is there a better and more efficient way to do this?
Instead of thinking of it as 'extracting', I like to think of it as 'isolating'. Once the desired bits are isolated, you can do what you will with them.
To isolate any set of bits, apply an AND mask.
If you want the last X bits of a value, there is a simple trick that can be used.
unsigned mask;
mask = (1 << X) - 1;
lastXbits = value & mask;
If you want to isolate a run of X bits in the middle of 'value' starting at 'startBit' ...
unsigned mask;
mask = ((1 << X) - 1) << startBit;
isolatedXbits = value & mask;
Hope this helps.
If you want n bits specific then you could first create a bitmask and then AND it with your number to take the desired bits.
Simple function to create mask from bit a to bit b.
unsigned createMask(unsigned a, unsigned b)
{
unsigned r = 0;
for (unsigned i=a; i<=b; i++)
r |= 1 << i;
return r;
}
You should check that a<=b.
If you want bits 12 to 16 call the function and then simply & (logical AND) r with your number N
r = createMask(12,16);
unsigned result = r & N;
If you want you can shift the result. Hope this helps
Modulus works to get bottom bits (only), although I think value & 0x1ffff expresses "take the bottom 17 bits" more directly than value % 131072, and so is easier to understand as doing that.
The top 17 bits of a 32-bit unsigned value would be value & 0xffff8000 (if you want them still in their positions at the top), or value >> 15 if you want the top 17 bits of the value in the bottom 17 bits of the result.
There is a single BEXTR (Bit field extract (with register)) x86 instruction on Intel and AMD CPUs and UBFX on ARM. There are intrinsic functions such as _bextr_u32() (link requires sign-in) that allow to invoke this instruction explicitly.
They implement (source >> offset) & ((1 << n) - 1) C code: get n continuous bits from source starting at the offset bit. Here's a complete function definition that handles edge cases:
#include <limits.h>
unsigned getbits(unsigned value, unsigned offset, unsigned n)
{
const unsigned max_n = CHAR_BIT * sizeof(unsigned);
if (offset >= max_n)
return 0; /* value is padded with infinite zeros on the left */
value >>= offset; /* drop offset bits */
if (n >= max_n)
return value; /* all bits requested */
const unsigned mask = (1u << n) - 1; /* n '1's */
return value & mask;
}
For example, to get 3 bits from 2273 (0b100011100001) starting at 5-th bit, call getbits(2273, 5, 3)—it extracts 7 (0b111).
For example, say I want the first 17 bits of the 32-bit value; what is it that I should do?
unsigned first_bits = value & ((1u << 17) - 1); // & 0x1ffff
Assuming CHAR_BIT * sizeof(unsigned) is 32 on your system.
I presume I am supposed to use the modulus operator and I tried it and was able to get the last 8 bits and last 16 bits
unsigned last8bitsvalue = value & ((1u << 8) - 1); // & 0xff
unsigned last16bitsvalue = value & ((1u << 16) - 1); // & 0xffff
If the offset is always zero as in all your examples in the question then you don't need the more general getbits(). There is a special cpu instruction BLSMSK that helps to compute the mask ((1 << n) - 1).
If you need the X last bits of your integer, use a binary mask :
unsigned last8bitsvalue=(32 bit integer) & 0xFF
unsigned last16bitsvalue=(32 bit integer) & 0xFFFF
This is a briefer variation of the accepted answer: the function below extracts the bits from-to inclusive by creating a bitmask. After applying an AND logic over the original number the result is shifted so the function returns just the extracted bits.
Skipped index/integrity checks for clarity.
uint16_t extractInt(uint16_t orig16BitWord, unsigned from, unsigned to)
{
unsigned mask = ( (1<<(to-from+1))-1) << from;
return (orig16BitWord & mask) >> from;
}
Bitwise AND your integer with the mask having exactly those bits set that you want to extract. Then shift the result right to reposition the extracted bits if desired.
unsigned int lowest_17_bits = myuint32 & 0x1FFFF;
unsigned int highest_17_bits = (myuint32 & (0x1FFFF << (32 - 17))) >> (32 - 17);
Edit: The latter repositions the highest 17 bits as the lowest 17; this can be useful if you need to extract an integer from “within” a larger one. You can omit the right shift (>>) if this is not desired.
#define GENERAL__GET_BITS_FROM_U8(source,lsb,msb) \
((uint8_t)((source) & \
((uint8_t)(((uint8_t)(0xFF >> ((uint8_t)(7-((uint8_t)(msb) & 7))))) & \
((uint8_t)(0xFF << ((uint8_t)(lsb) & 7)))))))
#define GENERAL__GET_BITS_FROM_U16(source,lsb,msb) \
((uint16_t)((source) & \
((uint16_t)(((uint16_t)(0xFFFF >> ((uint8_t)(15-((uint8_t)(msb) & 15))))) & \
((uint16_t)(0xFFFF << ((uint8_t)(lsb) & 15)))))))
#define GENERAL__GET_BITS_FROM_U32(source,lsb,msb) \
((uint32_t)((source) & \
((uint32_t)(((uint32_t)(0xFFFFFFFF >> ((uint8_t)(31-((uint8_t)(msb) & 31))))) & \
((uint32_t)(0xFFFFFFFF << ((uint8_t)(lsb) & 31)))))))
int get_nbits(int num, int n)
{
return (((1<<n)-1) & num);
}
I have another method for accomplishing this. You can use a union of an integer type that has enough bits for your application and a bit field struct.
Example:
typedef thesebits
{
unsigned long first4 : 4;
unsigned long second4 : 4;
unsigned long third8 : 8;
unsigned long forth7 : 7;
unsigned long fifth3 : 3;
unsigned long sixth5 : 5;
unsigned long last1 : 1;
} thesebits;
you can set that struct up to whatever bit pattern you want. If you have multiple bit patterns, you can even use that in your union as well.
typedef thesebitstwo
{
unsigned long first8 : 8;
unsigned long second8 : 8;
unsigned long third8 : 8;
unsigned long last8 : 8;
} thesebitstwo;
Now you can set up your union:
typedef union myunion
{
unsigned long mynumber;
thesebits mybits;
thesebitstwo mybitstwo;
} myunion;
Then you can access the bits you want from any number you assign to the member mynumber:
myunion getmybits;
getmybits.mynumber = 1234567890;
If you want the last 8 bits:
last16bits = getmybits.mybitstwo.last8;
If you want the second 4 bits:
second4bits = getmybits.mybits.second4;
I gave two examples kind of randomly assigned different bits to show. You can set the struct bit-fields up for whatever bits you want to get. I made all of the variables type unsigned long but you can use any variable type as long as the number of bits doesn't exceed those that can be used in the type. So most of these could have been just unsigned int and some even could be unsigned short
The caveat here is this works if you always want the same set of bits over and over. If there's a reason you may need to vary which bits you're looking at to anything, you could use a struct with an array that keeps a copy of the bits like so:
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
typedef struct bits32
{
bool b0 : 1;
bool b1 : 1;
bool b2 : 1;
bool b3 : 1;
bool b4 : 1;
bool b5 : 1;
bool b6 : 1;
bool b7 : 1;
bool b8 : 1;
bool b9 : 1;
bool b10 : 1;
bool b11 : 1;
bool b12 : 1;
bool b13 : 1;
bool b14 : 1;
bool b15 : 1;
bool b16 : 1;
bool b17 : 1;
bool b18 : 1;
bool b19 : 1;
bool b20 : 1;
bool b21 : 1;
bool b22 : 1;
bool b23 : 1;
bool b24 : 1;
bool b25 : 1;
bool b26 : 1;
bool b27 : 1;
bool b28 : 1;
bool b29 : 1;
bool b30 : 1;
bool b31 : 1;
} bits32;
typedef struct flags32 {
union
{
uint32_t number;
struct bits32 bits;
};
bool b[32];
} flags32;
struct flags32 assignarray ( unsigned long thisnumber )
{
struct flags32 f;
f.number = thisnumber;
f.b[0] = f.bits.b0;
f.b[1] = f.bits.b1;
f.b[2] = f.bits.b2;
f.b[3] = f.bits.b3;
f.b[4] = f.bits.b4;
f.b[5] = f.bits.b5;
f.b[6] = f.bits.b6;
f.b[7] = f.bits.b7;
f.b[8] = f.bits.b8;
f.b[9] = f.bits.b9;
f.b[10] = f.bits.b10;
f.b[11] = f.bits.b11;
f.b[12] = f.bits.b12;
f.b[13] = f.bits.b13;
f.b[14] = f.bits.b14;
f.b[15] = f.bits.b15;
f.b[16] = f.bits.b16;
f.b[17] = f.bits.b17;
f.b[18] = f.bits.b18;
f.b[19] = f.bits.b19;
f.b[20] = f.bits.b20;
f.b[21] = f.bits.b21;
f.b[22] = f.bits.b22;
f.b[23] = f.bits.b23;
f.b[24] = f.bits.b24;
f.b[25] = f.bits.b25;
f.b[26] = f.bits.b26;
f.b[27] = f.bits.b27;
f.b[28] = f.bits.b28;
f.b[29] = f.bits.b29;
f.b[30] = f.bits.b30;
f.b[31] = f.bits.b31;
return f;
}
int main ()
{
struct flags32 bitmaster;
bitmaster = assignarray(1234567890);
printf("%d\n", bitmaster.number);
printf("%d\n",bitmaster.bits.b9);
printf("%d\n",bitmaster.b[9]);
printf("%lu\n", sizeof(bitmaster));
printf("%lu\n", sizeof(bitmaster.number));
printf("%lu\n", sizeof(bitmaster.bits));
printf("%lu\n", sizeof(bitmaster.b));
}
The issue with this last example is that it's not compact. The union itself is only 4 bytes, but since you can't do pointers to bit-fields (without complicated and debatably "non-standard" code), then the array makes a copy of each boolean value and uses a full byte for each one, instead of just the bit, so it takes up 9x the total memory space (if you run the printf statement examples I gave, you'll see).
But now, you can address each bit one-by-one and use a variable to index each one, which is great if you're not short on memory.
By using the typedefs above and the assignarray function as a constructor for flags32, you can easily expand to multiple variables. If you're OK just addressing with .b# and not being able to use a variable, you can just define the union as flags32 and omit the rest of the struct. Then you also don't need the assignarray function, and you'll use much less memory.

Resources